#Librerias
library(tidyverse)
library(tidymodels)
library(GGally)
library(ggplot2)
library(MASS)
library(robustbase)
library(dplyr)
library(corrplot)
library(caret)
library(viridis)
library(gridExtra)
library(kableExtra)

Objetivo

El fútbol es el deporte más popular, el que tiene un mercado más grande y en el cual el valor de las transferencias de los jugadores es el más alto. En ese sentido, nos proponemos analizar las transferencias y cotizaciones de los jugadores de la liga inglesa, una de las mejores ligas del mundo.

Por este motivo, y haciendo uso de los conocimientos aportados por la materia, optamos por profundizar en el desarrollo de métodos de regresión robustos para hacer análisis predictivos, haciendo foco en la explicabilidad del modelo para predecir las transferencias de los jugadores.

El objetivo del presente trabajo consiste en la utilización distintos métodos robustos para predecir la variable objetivo: el precio de transferencia de los jugadores.

  • Observar si el país de origen es una variable explicativa con respecto a la variable objetivo.
  • Identificar si los ratios que demuestran el desempeño de los jugadores generan impacto sobre la transferencia de los jugadores.
  • Utilizar la regresión regresión de mínimos cuadrados ponderados, regresión de mínimos cuadrados robustos (Huber).
  • Comparar el desempeño de los distintos modelos para predecir la variable objetivo.

Introducción

En el método lineal clásico se utiliza el método de cuadrados mínimos para encontrar los parámetros \(\beta\).

La función de pérdida que se quiere minimizar es la suma del cuadrado de los residuos.

\[ g(a, b) = \sum_{i=1}^n \left( Y_i - \left( a + b X_i \right) \right)^2 \quad \text{(1)} \]

En los modelos lineales robustos queremos cambiar la función de perdida tal que:

  • Sea Insensible a valores extremadamente grandes o outliers(o residuos grandes)
  • Crezca menos que mínimos cuadrados cuando miramos lo suficientemente lejos del cero
  • Tenga Alta eficiencia: si los datos de la muestra siguieran el modelo de regresión con errores normales, queremos que el estimador (beta) que el método robusta calcula se parezca al de mínimos cuadrados, por lo que la función de pérdida que el método robusto calcula debería parecerse al de mínimos cuadrado.

\[ g(a, b) = \sum_{i=1}^n \rho \left( \frac{Y_i - \left( a + b X_i \right)}{s_n} \right) \quad \text{(2)} \] Definición de \(\rho\)

-\(\rho\) : R →R.

Una función-\(\rho\) denotará a una función \(\rho\)(u) tal que:

-\(\rho\)(0) = 0.

-\(\rho\)(−u) = \(\rho\)(u).

-0 ≤ u ≤ v implica \(\rho\)(u) ≤ \(\rho\)(v).

-\(\rho\) es continua.

-sup~u \(\rho\)(u) = 1.

Si \(\rho\)(u) < 1 y 0 ≤ u < v entonces \(\rho\)(u) < \(\rho\)(v).

es una función acotada, creciente y simétrica alrededor del cero, y \(s_n\) es un estimador de escala que juega el papel de σ en el modelo clásico de regresión.

Una posibilidad es ajustar una recta usando un procedimiento de ajuste robusto, por ejemplo un MM-estimador de regresión, propuesto por Yohai [1987]. En R, esto está programado dentro de la rutina lmrob en el paquete robustbase de R. La estimación se hace en tres etapas, se propone un estimador inicial de los parámetros, a partir de él se estima a sn y manualmente se obtienen los estimadores de los parámetros a partir de ellos, minimizando la función objetivo

Existen varias funciones \(\rho\) que serán evaluadas en el presente trabajo. Por defecto, robustbase utiliza la bicuadrada (bisquare), pero también se pueden implementar lqq, welsh, optimal, etc.

Robustbase utiliza el algoritmo Iteratively Reweighted Least Squares (IRWLS) para estimar los parámetros \(\beta\). El proceso consiste en la siguientes etapas:

  1. Inicializar el proceso utilizando un estimación inicial de a y b utilizando el método de cuadrados mínimos.

  2. Derivar la función (2) respecto a los parámetros a y b: \[ \frac{\partial g}{\partial a} = \sum_{i=1}^{n} \psi \left(\frac{Y_i - (a + bX_i)}{s_n} \right) \cdot \frac{-1}{s_n} \quad \text{(3)} \] \[ \frac{\partial g}{\partial b} = \sum_{i=1}^{n} \psi \left(\frac{Y_i - (a + bX_i)}{s_n} \right) \cdot \frac{-X_i}{s_n} \quad \text{(4)} \]

donde

\[ \frac{d \rho(x)}{dx} = \psi(x) \quad \text{(5)} \]

  1. Se calculan los pesos de cada observacion

\[ w_i = \frac{\psi \left(\frac{Y_i - (a + bX_i)}{s} \right)}{\frac{Y_i - (a + bX_i)}{s}} \quad \text{(6)} \]

  1. Se multiplica a cada observación por su respectivo peso.

  2. Se vuelven a estimar los parámetros a y b con una regresión ponderada.

Se repiten los pases hasta no observar mas mejoras o hasta un máximo de iteraciones.

A continuación de muestras Las funciones \(\rho\) más comunes y como se comportan los pesos de las mismas.

  1. Bisquare (Tukey): \[ \rho(x) = \begin{cases} 1 - (1 - (x/k)^2)^3 & \text{si } |x| \leq k \\ 1 & \text{si } |x| > k \end{cases} \quad \text{(7)} \]
# set margins for plots
options(SweaveHooks=list(fig=function() par(mar=c(3,3,1.4,0.7),
                                            mgp=c(1.5, 0.5, 0))))
## x axis for plots:
x. <- seq(-5, 10, length.out = 1501)
source(system.file("xtraR/plot-psiFun.R", package = "robustbase", mustWork=TRUE))
getOption("SweaveHooks")[["fig"]]()
p.psiFun(x., "biweight", par = 4.685)

  1. Huber: \[ \rho(x) = \begin{cases} \frac{x^2}{2} & \text{si } |x| \leq k \\ k|x| - \frac{k^2}{2} & \text{si } |x| > k \end{cases} \quad \text{(8)} \]
getOption("SweaveHooks")[["fig"]]()
plot(huberPsi, x., ylim=c(-1.4, 5), leg.loc="topright", main=FALSE)

  1. Welsh: \[ \rho(x) = 1 - \exp(-x^2/k^2) \quad \text{(9)} \]
getOption("SweaveHooks")[["fig"]]()
p.psiFun(x., "Welsh", par = 2.11)

  1. Hampel: \[ \rho(x) = \begin{cases} \frac{x^2}{2} & \text{si } |x| \leq a \\ a|x| - \frac{a^2}{2} & \text{si } a < |x| \leq b \\ \frac{a(c|x| - x^2/2 - bc + b^2/2)}{c-b} & \text{si } b < |x| \leq c \\ a(c - \frac{b}{2}) & \text{si } |x| > c \end{cases} \quad \text{(10)} \]
getOption("SweaveHooks")[["fig"]]()
## see also hampelPsi
p.psiFun(x., "Hampel", par = ## Default, but rounded:
           round(c(1.5, 3.5, 8) * 0.9016085, 1))

  1. LQQ (Linearly Quadratically Quadratic): \[ \rho(x) = \begin{cases} \frac{x^2}{2} & \text{si } |x| \leq c_1 \\ c_1|x| - \frac{c_1^2}{2} & \text{si } c_1 < |x| \leq c_2 \\ \frac{(c_3|x| - x^2/2)}{c_3-c_2} & \text{si } c_2 < |x| \leq c_3 \\ c_3 & \text{si } |x| > c_3 \end{cases} \quad \text{(11)} \]
getOption("SweaveHooks")[["fig"]]()
p.psiFun(x., "LQQ", par = c(-.5,1.5,.95,NA))

  1. Optimal \[ \psi(x) = \begin{cases} x & \text{si } |x| \leq a \\ a \cdot \text{sign}(x) \cdot \left(1 - \left(\frac{|x| - a}{b - a}\right)^2\right)^2 & \text{si } b < |x| \leq c \\ 0 & \text{si } |x| > c \end{cases} \quad \text{(12)} \]

Donde \(k\), \(a\), \(b\), \(c\), \(c_1\), \(c_2\) y \(c_3\) son constantes que determinan los puntos de quiebre y la forma de cada función.

Existen muchas otras propuestas de estimadores robustos para regresión, por ejemplo LMS (least median of squares), LTS (least trimmed squares), τ−estimadores de regresión, y casi todas están implementadas en R.

Dataset

El set de datos consiste en dos bases, ambas obtenidas de Kaggle.

La base de datos con los precios de los jugadores se obtuvo de la web Transfermarkt. Cuenta con 32405 registros, y dos de sus variables son los precios actuales de los jugadores y el precio mas alta alcanzado. Además cuenta con la información del club y liga actual de cada jugador.

Las otra base de datos cuenta con las métricas durante la temporada 2023/2023 de los jugadores de las 5 ligas mas importantes del fútbol europeo. Algunas de sus variables son Edad, goles anotados, asistencias, posición dentro del campo de juego, nacionalidad, entre otras.

Ambas bases de datos se unieron por el nombre y apellido del jugador, y el club actual. Adicionalmente, se agregó la variable Continente al dataset final, la cual informa el continente natal de cada jugador.

El dataset se divide en Entrenamiento y Prueba, de manera estratificada según el precio. Se utiliza el siguiente boxplot para generar una nueva categoría que categorice según el precio del jugador.

Luego utilizaremos esta nueva variable para realizar la división del dataset de manera estratificada.

Categorías de la nueva variable

  • Muy bajo: Menor a mediana (Q2)
  • Bajo: entre mediana (Q2) y cuartil superior (Q3)
  • Medio: entre cuartil superior (Q3) y límite superior (Q3 + 1.5 x IQR)
  • Alto: entre limite superior y €100.000.000
  • Muy Alto: mayor a €100.000.000 (representa los 10 jugadores mas caros)

El objetivo es que quedan estratificados los jugadores mas caros u outliers, por eso no se realiza una división por debajo de Q2.

# Carga de datset
df <- as.data.frame(read.csv("dataset.csv"))
df <- as.data.frame(df)
df <- na.omit(df)
colnames(df)[colnames(df) == "market_value_in_eur"] <- "precio"

# Agregar nueva variable que tenga en cuenta los precios de los jugadores y poder hacer un split 
# test-train estratificado
caja_precios <- boxplot(df$precio)

df$precio_cat <- NA

for (i in 1:length(df$precio)) {
  if (df$precio[i] >= 100000000) { 
    df$precio_cat[i] <- "muy_alto"}
  if (df$precio[i] < 100000000 && df$precio[i] >= caja_precios$stats[5]) {
    df$precio_cat[i] <- "alto"}
  if (df$precio[i] < caja_precios$stats[5] && df$precio[i] >= caja_precios$stats[4]) {
    df$precio_cat[i] <- "medio"}
  if (df$precio[i] < caja_precios$stats[4] && df$precio[i] >= caja_precios$stats[3]) {
    df$precio_cat[i] <- "bajo"}
  if (df$precio[i] < caja_precios$stats[3]){
    df$precio_cat[i] <- "muy_bajo"}
}

# Suponiendo que `df$clase` es la variable categórica
set.seed(28749658)  # Fijar semilla para reproducibilidad

# Crear índices estratificados basados en la variable `clase`
train_indices <- createDataPartition(df$precio_cat, p = 0.7, list = FALSE)  # 70% entrenamiento

# Dividir los datos
train_data <- df[train_indices, ]
test_data <- df[-train_indices, ]

Verificar la distribución de clases

Datos de Entrenamiento

table(train_data$precio_cat)
## 
##     alto     bajo    medio muy_alto muy_bajo 
##      132      298      171       10      546

Datos de prueba

table(test_data$precio_cat)
## 
##     alto     bajo    medio muy_alto muy_bajo 
##       56      127       73        4      234

Análisis Exploratorio de Datos

Gráficos de barras de algunas variables categóricas

colnames(df)[colnames(df) == "market_value_in_eur"] <- "precio"
df$continente <- factor(df$continente, levels = c("europa", "america", "africa", "asia_oceania"))
grafico1 <- ggplot(df, aes(x=continente, fill = continente)) +
  geom_bar() +
  scale_fill_viridis(discrete = TRUE, option = "D") + # Paleta accesible
  labs(y = "Cantidad", 
       x = "Continente",
       title = "Cantidad de jugadores por continente") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle=45, vjust=1, hjust=1),
        legend.position = "none")

df$Comp <- factor(df$Comp, levels = c("es La Liga", "it Serie A", "fr Ligue 1", "de Bundesliga",
                                      "eng Premier League"))
grafico2 <- ggplot(df, aes(x=Comp, fill = Comp)) + 
  geom_bar() +
  scale_fill_viridis(discrete = TRUE, option = "D") + # Paleta accesible
  labs(y = "Cantidad", x = "Liga", title = "Cantidad de jugadores por liga") +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle=45, vjust=1, hjust=1),
        legend.position = "none")

posiciones <- table(df$position)
posiciones <- names(sort(posiciones))
df$position <- factor(df$position, levels = posiciones)
grafico3 <- ggplot(df, aes(x=position, fill = position)) + 
  geom_bar() +
  scale_fill_viridis(discrete = TRUE, option = "D") + # Paleta accesible
  labs(y = "Count", x = "Liga", title = "Cantidad de jugadores por posicion") +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle=45, vjust=1, hjust=1,),
        legend.position = "none")

grafico4 <- ggplot(df, aes(x=foot, fill = foot)) + 
  geom_bar() +
  scale_fill_viridis(discrete = TRUE, option = "D") + # Paleta accesible
  labs(y = "Count", x = "Liga", title = "Cantidad de jugadores por pie habil") +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle=45, vjust=1, hjust=1,),
        legend.position = "none")

grid.arrange(grafico1, grafico2, grafico3, grafico4,nrow = 2)

Jugadores por continente

Como era de esperarse, predominan los jugadores europeos. Los americanos y africanos están casi en igual medida, siendo un poco mayor los americanos. Asia y Oceanía en conjunto aportan solo una mínima cantidad.

Cantidad de jugadores de liga

Todas las ligas están igual de representadas en el dataset, con lo cual el análisis del precio de jugadores según la liga será de interés.

Jugadores por posición

Predominan los jugadores que se desempeñan en el centro del campo, como defensores centrales o mediocampistas centrales o mixtos. Esto tiene cierta lógica ya que 3 o 4 jugadores de campo siempre se desempeñan en estas posiciones. Llama la atención la alta proporción de centrodelanteros, ya que los equipos suelen usar un solo jugador en esa posición.

Jugadores por pie hábil.

Como era de esperarse, mas de la mitad de los jugadores son derechos. Sin embargo, comparado con la proporción de personas diestras en el mundo (85%), la cantidad de jugadores zurdos es bastante mayor a estar proporción.

Correlograma

df %>% 
  dplyr::select(Age, Gls, Ast, precio) %>% 
  mutate(liga = df$current_club_domestic_competition_id) %>%
  ggpairs(., mapping = aes(colour = liga),
          upper = list(continuous = wrap("cor", size = 3, hjust=0.5)), progress=FALSE) + 
  scale_color_viridis_d(option = "D") + 
  scale_fill_viridis_d(option = "D") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1), legend.position = "bottom") + 
  theme_bw() +
  labs(title='Correlograma variables continuas')

Correlación entre Goles y Precio

Se observa una correlación positiva alta a moderada (Corr: 0.502). Esto sugiere que los jugadores que marcan más goles tienden a tener un valor de mercado más alto. La relación parece ser consistente a través de las diferentes ligas.

Correlación entre Asistencias y Precio

Existe una correlación positiva moderada (Corr: 0.465). Indica que los jugadores que dan más asistencias también tienden a tener un mayor valor en el mercado. La correlación es algo menor que con los goles, sugiriendo que el mercado valora más la capacidad goleadora.

Correlación entre Edad y Precio

Se observa una correlación negativa baja pero clara (Corr: -0.176). Sugiere que el valor de mercado tiende a disminuir con la edad del jugador. Esto tiene sentido desde una perspectiva de inversión, ya que los jugadores más jóvenes tienen mayor potencial de desarrollo y años de carrera por delante.

Distribución por Ligas

Los diagramas de caja (boxplots) muestran diferencias en la distribución de precios entre ligas. Las ligas española y británica presentan valores más altos en general. Esto se alinea con el poder económico de estas ligas y específicamente de clubes como Real Madrid y Manchester City. La distribución de precios es notablemente asimétrica, con algunos valores muy altos que podrían considerarse outliers.

Correlaciones cruzadas

Existe una correlación positiva moderada entre goles y asistencias (Corr: 0.586). Esto sugiere que los jugadores más efectivos tienden a destacar tanto en goles como en asistencias. La edad muestra correlaciones muy débiles con goles y asistencias.

Jugadores mas caros

Los 10 jugadores mas caros

mas_caros_nombre <- df %>% 
  slice_max(order_by = precio, n=10)  %>% 
  pull(name)
mas_caros_equipo <- df %>% 
  slice_max(order_by = precio, n=10)  %>% 
  pull(Squad)
mas_caros_precio <- df %>% 
  slice_max(order_by = precio, n=10)  %>% 
  pull(precio)
mas_caro <- data.frame(Nombre = mas_caros_nombre, 
                       Equipo = mas_caros_equipo, 
                       Precio = mas_caros_precio)

kable(mas_caro)
Nombre Equipo Precio
erling haaland manchester city 2.0e+08
vinicius junior real madrid 2.0e+08
jude bellingham real madrid 1.8e+08
lamine yamal barcelona 1.5e+08
phil foden manchester city 1.5e+08
bukayo saka arsenal 1.4e+08
federico valverde real madrid 1.3e+08
florian wirtz leverkusen 1.3e+08
rodri manchester city 1.3e+08
declan rice arsenal 1.2e+08

Jugadores mas baratos

Los 10 jugadores mas baratos

mas_baratos_nombre <- df %>% 
  slice_min(order_by = precio, n=10)  %>% 
  pull(name)
mas_baratos_equipo <- df %>% 
  slice_min(order_by = precio, n=10)  %>% 
  pull(Squad)
mas_baratos_precio <- df %>% 
  slice_min(order_by = precio, n=10)  %>% 
  pull(precio)
mas_barato <- data.frame(Nombre = mas_baratos_nombre, 
                       Equipo = mas_baratos_equipo, 
                       Precio = mas_baratos_precio)

kable(mas_barato)
Nombre Equipo Precio
aurelien pelon lorient 50000
daouda traore nice 50000
sofiane sidi ali marseille 50000
steven baseya strasbourg 50000
yassin tallal getafe 50000
simone aresti cagliari 75000
adel mahamoud nantes 100000
antonio mirante milan 100000
daniele sommariva genoa 100000
dominic sadi bournemouth 100000
francesco rossi atalanta 100000
ichem ferrah lille 100000
ivan cuellar mallorca 100000
joel imasuen werder bremen 100000

Regresión Lineal Simple

Modelo “Goles son amores”

Precio = Goles

modelo_clasico_goles = lm(data = train_data, formula = precio ~ Gls)
summary(modelo_clasico_goles)
## 
## Call:
## lm(formula = precio ~ Gls, data = train_data)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -58318585  -5505828  -3505828   2261548 112681415 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  5238452     519499   10.08   <2e-16 ***
## Gls          3267375     147163   22.20   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 15400000 on 1155 degrees of freedom
## Multiple R-squared:  0.2991, Adjusted R-squared:  0.2985 
## F-statistic: 492.9 on 1 and 1155 DF,  p-value: < 2.2e-16
ggplot(df, aes(x= Gls, y=precio))+
  geom_point() +
  theme_bw() +
  geom_smooth(method = "lm", formula = y ~ x, color="forestgreen", se = FALSE)

Diagnóstico

datos_augmentados <- augment(modelo_clasico_goles)
g1 <- ggplot(datos_augmentados, aes(.fitted, .resid)) +
  geom_point() +
  geom_hline(yintercept = 0) +
  geom_smooth(se = FALSE) +
  labs(title = "Residuos vs valores predichos") + 
  theme_bw()
g2 <- ggplot(datos_augmentados, aes(sample = .std.resid)) +
  stat_qq() +
  geom_abline() +
  labs(title = "Normal QQ plot") + 
  theme_bw()
g3 <- ggplot(datos_augmentados, aes(.fitted, sqrt(abs(.std.resid)))) +
  geom_point() +
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Scale-location plot")
g4 <- ggplot(datos_augmentados, aes(.hat, .std.resid)) +
  geom_vline(size = 2, colour = "white", xintercept = 0) +
  geom_hline(size = 2, colour = "white", yintercept = 0) +
  geom_point() + 
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Residual vs leverage")
grid.arrange(g1, g2, g3, g4, nrow=2)

Diagnóstico de Residuos

  • El gráfico de residuos vs valores predichos muestra un patrón de embudo, indicando heterocedasticidad
  • El QQ-plot muestra desviaciones significativas de la normalidad, especialmente en las colas
  • El Scale-location plot confirma la heterocedasticidad, con mayor variabilidad en los valores predichos más altos
  • El gráfico de leverage muestra varios puntos con alta influencia que podrían estar afectando el modelo

Interpretación del Modelo

  • El modelo establece una relación lineal simple entre el precio del jugador y los goles anotados
  • Por cada gol anotado, se precibe un aumento estadísticamente significativo (p-value < 0.05) en el precio del jugador de €3,267,375
  • El intercepto es €5,238,452, que representa el precio base estimado para un jugador sin goles
  • El modelo es estadísticamente significativo (p-value < 2.2e-16)
  • El R² ajustado es 0.2985, lo que indica que el modelo explica aproximadamente el 30% de la variabilidad en los precios

Evaluación del Rendimiento

Datos de entrenamiento
pred_modelo_clasico_goles <- augment(modelo_clasico_goles, newdata = train_data)
cat("RMSE: ", rmse(data = pred_modelo_clasico_goles, truth = precio, estimate = .fitted)$.estimate)
## RMSE:  15388866
cat("MAE: ", mae(data = pred_modelo_clasico_goles, truth = precio, estimate = .fitted)$.estimate)
## MAE:  9179813
Datos de prueba
pred_modelo_clasico_goles <- augment(modelo_clasico_goles, newdata = test_data)
cat("RMSE: ", rmse(data = pred_modelo_clasico_goles, truth = precio, estimate = .fitted)$.estimate)
## RMSE:  17503869
cat("MAE: ", mae(data = pred_modelo_clasico_goles, truth = precio, estimate = .fitted)$.estimate)
## MAE:  9732598
  • En datos de entrenamiento:
    • RMSE: 15,388,866
    • MAE: 9,179,813
  • En datos de prueba:
    • RMSE: 17,503,869
    • MAE: 9,732,598
  • La diferencia relativamente pequeña entre los errores de entrenamiento y prueba sugiere que el modelo no está sobreajustado

Limitaciones del Modelo

  • La relación lineal simple puede ser demasiado básica para capturar la complejidad del precio de los jugadores
  • La presencia de heterocedasticidad sugiere que la variabilidad del precio aumenta con el número de goles
  • El bajo R² indica que hay otros factores importantes que no están siendo considerados
  • Los supuestos de normalidad y homocedasticidad no se cumplen adecuadamente

Este modelo, aunque estadísticamente significativo, tiene limitaciones importantes para predecir el precio de los jugadores. La violación de los supuestos básicos y el bajo poder explicativo sugieren que se necesita un modelo más complejo que incorpore variables adicionales y posiblemente transformaciones de las variables existentes.

Modelo “La edad NO es lo de menos”

Precio = \(Edad\) + \(Edad^2\)

modelo_clasico_edad = modelo_clasico_edad2 = lm(data = train_data, formula = precio ~ Age + I(Age^2))
summary(modelo_clasico_edad)
## 
## Call:
## lm(formula = precio ~ Age + I(Age^2), data = train_data)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -13491197  -9734927  -5198104   2200547 186201791 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -45547305   13549572  -3.362 0.000801 ***
## Age           5113615    1054307   4.850 1.40e-06 ***
## I(Age^2)      -110147      20094  -5.482 5.18e-08 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 17940000 on 1154 degrees of freedom
## Multiple R-squared:  0.05018,    Adjusted R-squared:  0.04853 
## F-statistic: 30.48 on 2 and 1154 DF,  p-value: 1.255e-13
ggplot(df, aes(x= Age, y=precio))+
  geom_point() +
  theme_bw() +
  geom_smooth(method = "lm", formula = y ~ x + I(x^2), color="forestgreen", se = FALSE)

cat("A partir de los", round(5113615/(2*110147),0), "años, el precio de los jugadores comienza a disminuir")
## A partir de los 23 años, el precio de los jugadores comienza a disminuir

Diagnóstico

datos_augmentados <- augment(modelo_clasico_edad)
g1 <- ggplot(datos_augmentados, aes(.fitted, .resid)) +
  geom_point() +
  geom_hline(yintercept = 0) +
  geom_smooth(se = FALSE) +
  labs(title = "Residuos vs valores predichos") + 
  theme_bw()
g2 <- ggplot(datos_augmentados, aes(sample = .std.resid)) +
  stat_qq() +
  geom_abline() +
  labs(title = "Normal QQ plot") + 
  theme_bw()
g3 <- ggplot(datos_augmentados, aes(.fitted, sqrt(abs(.std.resid)))) +
  geom_point() +
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Scale-location plot")
g4 <- ggplot(datos_augmentados, aes(.hat, .std.resid)) +
  geom_vline(size = 2, colour = "white", xintercept = 0) +
  geom_hline(size = 2, colour = "white", yintercept = 0) +
  geom_point() + 
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Residual vs leverage")
grid.arrange(g1, g2, g3, g4, nrow=2)

Diagnóstico de Residuos

  • El gráfico de residuos vs valores predichos muestra un patrón de embudo similar al modelo anterior
  • El QQ-plot muestra desviaciones significativas de la normalidad, especialmente en las colas superiores
  • El Scale-location plot indica heterocedasticidad
  • El gráfico de leverage muestra algunos puntos influyentes, aunque menos pronunciados que en el modelo de goles

Interpretación del Modelo

  • El modelo establece una relación cuadrática entre el precio del jugador y su edad
  • El precio aumenta con la edad hasta los 23 años, punto a partir del cual comienza a disminuir
  • Los coeficientes son:
    • Edad: +5,113,615 (efecto lineal positivo)
    • Edad²: -110,147 (efecto cuadrático negativo)
    • Intercepto: -45,547,305
  • El modelo es estadísticamente significativo (p-value: 1.255e-13)
  • El R² ajustado es 0.04853, lo que indica que el modelo explica solo aproximadamente el 5% de la variabilidad en los precios

Evaluación del Rendimiento

Datos de entrenamiento
pred_modelo_clasico_edad <- augment(modelo_clasico_edad, newdata = train_data)
cat("RMSE: ", rmse(data = pred_modelo_clasico_edad, truth = precio, estimate = .fitted)$.estimate)
## RMSE:  17914627
cat("MAE: ", mae(data = pred_modelo_clasico_edad, truth = precio, estimate = .fitted)$.estimate)
## MAE:  10519126
Datos de prueba
pred_modelo_clasico_edad <- augment(modelo_clasico_edad, newdata = test_data)
cat("RMSE: ", rmse(data = pred_modelo_clasico_edad, truth = precio, estimate = .fitted)$.estimate)
## RMSE:  18482683
cat("MAE: ", mae(data = pred_modelo_clasico_edad, truth = precio, estimate = .fitted)$.estimate)
## MAE:  10655848
  • En datos de entrenamiento:
    • RMSE: 17,914,627
    • MAE: 10,519,126
  • En datos de prueba:
    • RMSE: 18,482,683
    • MAE: 10,655,848
  • Los errores son mayores que en el modelo de goles, sugiriendo que la edad por sí sola es un predictor más débil del precio

Limitaciones del Modelo

  • El muy bajo R² sugiere que la edad por sí sola no es un buen predictor del precio
  • La relación cuadrática captura el hecho de que los jugadores alcanzan un pico de valor, pero el ajuste general es pobre
  • Los supuestos de normalidad y homocedasticidad siguen sin cumplirse
  • El modelo no captura otros factores importantes que afectan el precio

Este modelo confirma que existe una relación no lineal entre la edad y el precio de los jugadores, con un punto máximo alrededor de los 23 años. Sin embargo, su bajo poder explicativo sugiere que la edad debe combinarse con otras variables para obtener predicciones más precisas.

Regresión Lineal Múltiple

Modelo “Ahora va en serio”

\(Precio\) = \(Goles\) + \(Edad\) + \(Edad^2\) + \(Asistencias\) + \(Continente\) \(de\) \(nacimiento\) \(del\) \(jugador\) + \(Liga\) \(donde\) \(juega\)

modelo_clasico_multiple_1 = lm(data = train_data, 
                             formula = precio ~ Gls + Age + I(Age^2) + Ast + 
                               continente + current_club_domestic_competition_id)
summary(modelo_clasico_multiple_1)
## 
## Call:
## lm(formula = precio ~ Gls + Age + I(Age^2) + Ast + continente + 
##     current_club_domestic_competition_id, data = train_data)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -37702078  -5979937  -1352304   4179402 117568853 
## 
## Coefficients:
##                                         Estimate Std. Error t value Pr(>|t|)
## (Intercept)                             -8999579   10503367  -0.857 0.391719
## Gls                                      2189855     160682  13.629  < 2e-16
## Age                                      1607300     804050   1.999 0.045844
## I(Age^2)                                  -44966      15312  -2.937 0.003384
## Ast                                      2152607     244624   8.800  < 2e-16
## continenteamerica                        4494113    1848911   2.431 0.015223
## continenteasia_oceania                   2283362    3956794   0.577 0.564003
## continenteeuropa                         2748364    1459721   1.883 0.059981
## current_club_domestic_competition_idFR1 -4436917    1268565  -3.498 0.000487
## current_club_domestic_competition_idGB1 11060116    1286087   8.600  < 2e-16
## current_club_domestic_competition_idIT1 -2429060    1229943  -1.975 0.048515
## current_club_domestic_competition_idL1  -4011806    1265818  -3.169 0.001568
##                                            
## (Intercept)                                
## Gls                                     ***
## Age                                     *  
## I(Age^2)                                ** 
## Ast                                     ***
## continenteamerica                       *  
## continenteasia_oceania                     
## continenteeuropa                        .  
## current_club_domestic_competition_idFR1 ***
## current_club_domestic_competition_idGB1 ***
## current_club_domestic_competition_idIT1 *  
## current_club_domestic_competition_idL1  ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 13350000 on 1145 degrees of freedom
## Multiple R-squared:  0.4783, Adjusted R-squared:  0.4733 
## F-statistic: 95.43 on 11 and 1145 DF,  p-value: < 2.2e-16

Diagnóstico

datos_augmentados <- augment(modelo_clasico_multiple_1)
g5 <- ggplot(datos_augmentados, aes(.fitted, .resid)) +
  geom_point() +
  geom_hline(yintercept = 0) +
  geom_smooth(se = FALSE) +
  labs(title = "Residuos vs valores predichos") + 
  theme_bw()
g6 <- ggplot(datos_augmentados, aes(sample = .std.resid)) +
  stat_qq() +
  geom_abline() +
  labs(title = "Normal QQ plot") + 
  theme_bw()
g7 <- ggplot(datos_augmentados, aes(.fitted, sqrt(abs(.std.resid)))) +
  geom_point() +
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Scale-location plot")
g8 <- ggplot(datos_augmentados, aes(.hat, .std.resid)) +
  geom_vline(size = 2, colour = "white", xintercept = 0) +
  geom_hline(size = 2, colour = "white", yintercept = 0) +
  geom_point() + 
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Residual vs leverage")
grid.arrange(g5, g6, g7, g8, nrow=2)

Diagnóstico de Residuos

  • El gráfico de residuos vs valores predichos muestra una mejora en el patrón de heterocedasticidad
  • El QQ-plot indica una mejor aproximación a la normalidad en el centro de la distribución
  • El Scale-location plot muestra una variabilidad más estable que los modelos anteriores
  • El gráfico de leverage identifica menos puntos influyentes extremos

Interpretación del Modelo

  • El modelo incorpora múltiples variables predictoras: goles, edad (lineal y cuadrática), asistencias, continente de origen y liga
  • Los coeficientes más significativos son:
    • Goles: +2,189,855 por gol (p-value < 2e-16). Esto indica que el precio de los jugadores que tienen misma cantidad de asistencias y edad aumenta €2,189,855 por cada gol anotado
    • Asistencias: +2,152,607 por asistencia (p-value < 2e-16). Esto indica que el precio de los jugadores que tienen misma cantidad de goles y edad aumenta €2,152,607 por cada asistencia adicional.
    • Edad: efecto cuadrático con máximo alrededor de los 23 años
    • América: +4,494,113 respecto a África (p-value = 0.015)
    • Premier League: +11,060,116 respecto a la liga de referencia, La Liga (España) (p-value < 2e-16)
    • Ligue 1: -4,436,917 respecto a la liga de referencia, La Liga (p-value < 0.05)
    • Bundesliga: -4,011,806 respecto a la liga de referencia, La Liga (p-value < 0.05)
    • Serie A (Italia): -2,429,060 respecto a la liga de referencia, La Liga (p-value < 0.05)
  • El R² ajustado es 0.4733, indicando que el modelo explica aproximadamente el 47% de la variabilidad

Evaluación del Rendimiento

Datos de entrenamiento
pred_modelo_clasico_multiple_1 <- augment(modelo_clasico_multiple_1, newdata = train_data)
cat("RMSE: ", rmse(data = pred_modelo_clasico_multiple_1, truth = precio, estimate = .fitted)$.estimate)
## RMSE:  13276913
cat("MAE: ", mae(data = pred_modelo_clasico_multiple_1, truth = precio, estimate = .fitted)$.estimate)
## MAE:  8093280
Datos de prueba
pred_modelo_clasico_multiple_1 <- augment(modelo_clasico_multiple_1, newdata = test_data)
cat("RMSE: ", rmse(data = pred_modelo_clasico_multiple_1, truth = precio, estimate = .fitted)$.estimate)
## RMSE:  15450180
cat("MAE: ", mae(data = pred_modelo_clasico_multiple_1, truth = precio, estimate = .fitted)$.estimate)
## MAE:  8469906
  • En datos de entrenamiento:
    • RMSE: 13,276,913
    • MAE: 8,093,280
  • En datos de prueba:
    • RMSE: 15,450,180
    • MAE: 8,469,906
  • La diferencia moderada entre errores de entrenamiento y prueba sugiere un nivel aceptable de generalización

Mejoras Respecto a Modelos Anteriores

  • El R² ajustado aumentó significativamente (de 0.30 y 0.05 a 0.47)
  • Los errores de predicción (RMSE y MAE) disminuyeron
  • Los diagnósticos de residuos muestran mejores propiedades estadísticas
  • La incorporación de variables categóricas captura efectos específicos por continente y liga

Este modelo representa una mejora sustancial sobre los modelos simples anteriores, capturando efectos más complejos y reduciendo los errores de predicción. Sin embargo, aún hay espacio para mejoras, especialmente en el tratamiento de valores extremos y la posible incorporación de más variables relevantes.

Modelo “Ahora va en serio 2”

\(log(Precio)\) = \(Goles\) + \(Edad\) + \(Edad^2\) + \(Asistencias\) + \(Continente\) \(de\) \(nacimiento\) \(del\) \(jugador\) + \(Liga\) \(donde\) \(juega\)

Al utilizar una transformación logarítmica, la interpretación del modelo es diferente. La variación de la variable a predecir es en términos porcentuales según aumenta en una unidad la variable predictora numérica o según la variable de referencia categórica.

Por lo tanto:

\[ c = (\exp(b) - 1) \times 100 \quad \text{(13)} \]

Donde

\[ y = a + b \times x \quad \text{(14)} \]

En este caso, si \(x\) es una variable numérica entonces \(y\) varía \(c\)% por cada aumento de \(x\) en una unidad. Si en cambio \(x\) es una variable categórica, \(y\) varía \(c\)% con respecto a la variable categórica de referencia.

modelo_clasico_multiple = lm(data = train_data, 
                                formula = log(precio) ~ Gls + Age + I(Age^2) + Ast + 
                                  continente + current_club_domestic_competition_id)
summary(modelo_clasico_multiple)
## 
## Call:
## lm(formula = log(precio) ~ Gls + Age + I(Age^2) + Ast + continente + 
##     current_club_domestic_competition_id, data = train_data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -4.0055 -0.6931  0.0515  0.7146  3.5105 
## 
## Coefficients:
##                                          Estimate Std. Error t value Pr(>|t|)
## (Intercept)                              5.573579   0.844203   6.602 6.19e-11
## Gls                                      0.116194   0.012915   8.997  < 2e-16
## Age                                      0.787591   0.064625  12.187  < 2e-16
## I(Age^2)                                -0.016285   0.001231 -13.232  < 2e-16
## Ast                                      0.172888   0.019662   8.793  < 2e-16
## continenteamerica                        0.404139   0.148605   2.720  0.00664
## continenteasia_oceania                   0.007232   0.318025   0.023  0.98186
## continenteeuropa                         0.041694   0.117324   0.355  0.72237
## current_club_domestic_competition_idFR1 -0.296188   0.101960  -2.905  0.00374
## current_club_domestic_competition_idGB1  0.817675   0.103369   7.910 6.02e-15
## current_club_domestic_competition_idIT1 -0.027229   0.098856  -0.275  0.78303
## current_club_domestic_competition_idL1  -0.285435   0.101740  -2.806  0.00511
##                                            
## (Intercept)                             ***
## Gls                                     ***
## Age                                     ***
## I(Age^2)                                ***
## Ast                                     ***
## continenteamerica                       ** 
## continenteasia_oceania                     
## continenteeuropa                           
## current_club_domestic_competition_idFR1 ** 
## current_club_domestic_competition_idGB1 ***
## current_club_domestic_competition_idIT1    
## current_club_domestic_competition_idL1  ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1.073 on 1145 degrees of freedom
## Multiple R-squared:  0.467,  Adjusted R-squared:  0.4619 
## F-statistic: 91.21 on 11 and 1145 DF,  p-value: < 2.2e-16

Diagnóstico

datos_augmentados <- augment(modelo_clasico_multiple)
g9 <- ggplot(datos_augmentados, aes(.fitted, .resid)) +
  geom_point() +
  geom_hline(yintercept = 0) +
  geom_smooth(se = FALSE) +
  labs(title = "Residuos vs valores predichos") + 
  theme_bw()
g10 <- ggplot(datos_augmentados, aes(sample = .std.resid)) +
  stat_qq() +
  geom_abline() +
  labs(title = "Normal QQ plot") + 
  theme_bw()
g11 <- ggplot(datos_augmentados, aes(.fitted, sqrt(abs(.std.resid)))) +
  geom_point() +
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Scale-location plot")
g12 <- ggplot(datos_augmentados, aes(.hat, .std.resid)) +
  geom_vline(size = 2, colour = "white", xintercept = 0) +
  geom_hline(size = 2, colour = "white", yintercept = 0) +
  geom_point() + 
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Residual vs leverage")
grid.arrange(g9, g10, g11, g12, nrow=2)

Diagnóstico de Residuos

  • El gráfico de residuos vs valores predichos muestra una distribución más homogénea
  • El QQ-plot indica una notable mejora en la normalidad de los residuos
  • El Scale-location plot muestra una varianza más estable
  • El gráfico de leverage sugiere menos influencia de valores extremos

Interpretación del Modelo

  • El modelo utiliza la transformación logarítmica de la variable precio y mantiene las mismas variables predictoras
  • Los coeficientes más significativos son:
    • Goles: +0.116 (p-value < 2e-16).Esto indica que el precio de los jugadores que tienen misma cantidad de asistencias y edad aumenta un 12.3% por cada gol anotado.
    • Asistencias: +0.173 (p-value < 2e-16). Esto indica que el precio de los jugadores que tienen misma cantidad de goles y edad aumenta un 18.9% por cada asistencia adicional.
    • Edad: efecto cuadrático significativo (p-value < 2e-16)
    • América: +0.404 respecto a África (p-value = 0.007), indica un aumento estadísticamete significativo de 49.8% en el precio de los jugadores americanos con respecto a los africanos.
    • Premier League: +0.818 respecto a la liga de referencia, (p-value < 2e-16) indica un aumento estadísticamente significativo de 126.5% en el precio de los jugadores de la Premier League con respecto a los jugadores de La Liga (España)
    • Ligue 1 (Francia): -0.296 respecto a la liga de referencia, (p-value = 0.003) indica una disminución estadísticamente significativa de 25.6% en el precio de los jugadores de la Ligue 1 con respecto a los jugadores de La Liga.
    • Bundesliga: -0.285 respecto de la liga de referencia (p-value = 0.005) indica una disminución estadísticamente significativa de 24.8% en el preicio de los jugadores de la Bundesliga con respecto a La Liga.
  • El R² ajustado es 0.4619, similar al modelo anterior pero con mejor interpretabilidad

Evaluación del Rendimiento

Datos de entrenamiento
pred_modelo_clasico_multiple <- augment(modelo_clasico_multiple, newdata = train_data)
pred_modelo_clasico_multiple$exp_fitted <- exp(pred_modelo_clasico_multiple$.fitted)
cat("RMSE: ", rmse(data = pred_modelo_clasico_multiple, truth = precio, estimate = exp_fitted)$.estimate)
## RMSE:  28500598
cat("MAE: ", mae(data = pred_modelo_clasico_multiple, truth = precio, estimate = exp_fitted)$.estimate)
## MAE:  8057781
Datos de prueba
pred_modelo_clasico_multiple <- augment(modelo_clasico_multiple, newdata = test_data)
pred_modelo_clasico_multiple$exp_fitted <- exp(pred_modelo_clasico_multiple$.fitted)
cat("RMSE: ", rmse(data = pred_modelo_clasico_multiple, truth = precio, estimate = exp_fitted)$.estimate)
## RMSE:  17508456
cat("MAE: ", mae(data = pred_modelo_clasico_multiple, truth = precio, estimate = exp_fitted)$.estimate)
## MAE:  7357616
  • En datos de entrenamiento:
    • RMSE: 28,500,598
    • MAE: 8,057,781
  • En datos de prueba:
    • RMSE: 17,508,456
    • MAE: 7,357,616
  • La transformación logarítmica mejora el MAE en los datos de prueba, aunque el RMSE es más alto

Mejoras Respecto al Modelo Anterior

  • Mejor interpretabilidad de los coeficientes en términos de porcentajes
  • Mejor cumplimiento de los supuestos de normalidad y homocedasticidad
  • Reducción del MAE en datos de prueba
  • Mayor estabilidad en la predicción de valores extremos

La transformación logarítmica del precio mejora las propiedades estadísticas del modelo y facilita la interpretación de los efectos. Este modelo parece más adecuado para predecir el precio de los jugadores, especialmente cuando se considera la interpretabilidad y la estabilidad de las predicciones.

Regresión Lineal Múltiple Robusta

Modelo “Goles son amores ROBUSTOS”

Precio = Goles

Se utiliza el modelo base de Robustbase, donde la fución de pérdida utilizada es la bicuadrada y utiliza el estimador MM.

modelo_lmrob_goles <- lmrob(formula = precio ~ Gls, data=train_data)
pred_modelo_lmrob_goles <- data.frame(
  Gls = train_data$Gls,
  precio_pred = predict(modelo_lmrob_goles, newdata = train_data)
)
summary(modelo_lmrob_goles)
## 
## Call:
## lmrob(formula = precio ~ Gls, data = train_data)
##  \--> method = "MM"
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -11398849  -2040823    -40823   7178107 172914731 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  3040823     233384   13.03  < 2e-16 ***
## Gls           890535     123689    7.20 1.08e-12 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Robust residual standard error: 3632000 
## Multiple R-squared:  0.2183, Adjusted R-squared:  0.2177 
## Convergence in 23 IRWLS iterations
## 
## Robustness weights: 
##  158 observations c(3,19,23,52,55,58,67,76,80,83,89,92,105,112,130,132,141,143,144,156,161,163,164,167,172,173,175,178,190,200,204,212,216,218,226,232,248,253,256,262,265,271,283,287,288,289,290,292,301,309,310,316,319,321,322,327,333,338,339,341,354,366,367,368,372,384,385,388,390,407,433,461,463,474,476,482,484,488,489,500,501,510,526,537,543,563,564,565,575,578,584,586,593,597,602,608,630,634,640,646,662,666,674,691,695,696,705,713,736,753,754,760,764,774,793,799,800,801,806,811,813,823,833,853,857,867,870,871,875,901,913,917,918,929,934,953,954,962,963,965,968,973,982,1024,1036,1052,1053,1063,1070,1091,1096,1100,1123,1124,1132,1134,1138,1154)
##   are outliers with |weight| <= 4.7e-05 ( < 8.6e-05); 
##  44 weights are ~= 1. The remaining 955 ones are summarized as
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## 0.002176 0.877400 0.959300 0.855000 0.983700 0.999000 
## Algorithmic parameters: 
##        tuning.chi                bb        tuning.psi        refine.tol 
##         1.548e+00         5.000e-01         4.685e+00         1.000e-07 
##           rel.tol         scale.tol         solve.tol          zero.tol 
##         1.000e-07         1.000e-10         1.000e-07         1.000e-10 
##       eps.outlier             eps.x warn.limit.reject warn.limit.meanrw 
##         8.643e-05         4.911e-11         5.000e-01         5.000e-01 
##      nResample         max.it       best.r.s       k.fast.s          k.max 
##            500             50              2              1            200 
##    maxit.scale      trace.lev            mts     compute.rd fast.s.large.n 
##            200              0           1000              0           2000 
##                   psi           subsampling                   cov 
##            "bisquare"         "nonsingular"         ".vcov.avar1" 
## compute.outlier.stats 
##                  "SM" 
## seed : int(0)

El siguiente gráfico muestra la linea de regresión del modelo lineal clásico (violeta) frente a la regresión del modelo robusto (amarillo).

Se puede apreciar muy claramente como el modelo robusto tiene una pendiente mas baja ajustando en menor medida a los jugadores de mayor cotización.

ggplot(train_data, aes(x = Gls, y = precio)) +
  geom_point() +
  geom_smooth(method = "lm", formula = y ~ x, color="darkviolet", se = FALSE) +
  geom_line(data = pred_modelo_lmrob_goles, aes(x = Gls, y = precio_pred), color = "yellow") +
  theme_bw()

Interpretación del Modelo

  • El modelo establece una relación lineal simple entre el precio del jugador y los goles anotados
  • Por cada gol anotado, el precio del jugador aumenta en €890,535; mucho menor al estimado por el método clásico de €3,267,375
  • El intercepto es €3,040,824, que representa el precio base estimado para un jugador sin goles también presente una notable baja frente al del modelo clásico de €5,238,452
  • El R² ajustado es 0.2177, lo que indica que el modelo explica aproximadamente el 21.8% de la variabilidad en los precios, observando una dismunición considerable frente al 30% del modelo clásico.

Evaluación y comparación de modelos frente a datos de prueba

modelos_simples <- list(simple_1 = modelo_clasico_goles, 
                simple_2 = modelo_lmrob_goles)

lista_predicciones_testing = map(.x = modelos_simples, .f = augment, newdata = test_data) 


goles_clasico_test = lista_predicciones_testing$simple_1 %>%  
  metrics(truth=precio, estimate=.fitted) %>%
  mutate(.estimate=round(.estimate, 4))

goles_robusto_test = lista_predicciones_testing$simple_2 %>%  
  metrics(truth=precio, estimate=.fitted) %>%
  mutate(.estimate=round(.estimate, 4))


metrica_goles <- rbind(goles_clasico_test[c(1,3),], goles_robusto_test[c(1,3),])

modelitos_simples <- c(rep("Clasico - Goles",2),rep("Robusto - Goles",2))
metricas <- cbind(modelitos_simples, metrica_goles)
kable(metricas)
modelitos_simples .metric .estimator .estimate
Clasico - Goles rmse standard 17503869
Clasico - Goles mae standard 9732598
Robusto - Goles rmse standard 19142968
Robusto - Goles mae standard 8713804
  • El RMSE es menor para el modelo clásico, sin embargo, al estar trabajando con datos que tienen outliers lo correcto es compararlo contra los valores de MAE.
  • El MAE es menor para el modelo robusto, mostrando un mejor desempeño frente al modelo clásico.

Primero Modelo Multiple Robusto

\(log(Precio)\) = \(Goles\) + \(Edad\) + \(Edad^2\) + \(Asistencias\) + \(Continente\) \(de\) \(nacimiento\) \(del\) \(jugador\) + \(Liga\) \(donde\) \(juega\)

modelo_multiple_lmrob_2 <- lmrob(formula = log(precio) ~ Gls + Age + I(Age^2) + Ast + 
                                 continente + current_club_domestic_competition_id, 
                               data=train_data)
summary(modelo_multiple_lmrob_2)
## 
## Call:
## lmrob(formula = log(precio) ~ Gls + Age + I(Age^2) + Ast + continente + current_club_domestic_competition_id, 
##     data = train_data)
##  \--> method = "MM"
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -4.15651 -0.71534  0.01675  0.68707  3.45838 
## 
## Coefficients:
##                                          Estimate Std. Error t value Pr(>|t|)
## (Intercept)                              5.796426   1.235981   4.690 3.06e-06
## Gls                                      0.111085   0.010745  10.339  < 2e-16
## Age                                      0.772800   0.090728   8.518  < 2e-16
## I(Age^2)                                -0.016112   0.001655  -9.734  < 2e-16
## Ast                                      0.170219   0.014713  11.569  < 2e-16
## continenteamerica                        0.439570   0.130353   3.372 0.000771
## continenteasia_oceania                   0.065008   0.310255   0.210 0.834070
## continenteeuropa                         0.089042   0.106069   0.839 0.401378
## current_club_domestic_competition_idFR1 -0.231087   0.106080  -2.178 0.029578
## current_club_domestic_competition_idGB1  0.917136   0.110887   8.271 3.66e-16
## current_club_domestic_competition_idIT1 -0.014964   0.101669  -0.147 0.883015
## current_club_domestic_competition_idL1  -0.282461   0.098817  -2.858 0.004335
##                                            
## (Intercept)                             ***
## Gls                                     ***
## Age                                     ***
## I(Age^2)                                ***
## Ast                                     ***
## continenteamerica                       ***
## continenteasia_oceania                     
## continenteeuropa                           
## current_club_domestic_competition_idFR1 *  
## current_club_domestic_competition_idGB1 ***
## current_club_domestic_competition_idIT1    
## current_club_domestic_competition_idL1  ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Robust residual standard error: 1.024 
## Multiple R-squared:  0.4842, Adjusted R-squared:  0.4793 
## Convergence in 17 IRWLS iterations
## 
## Robustness weights: 
##  101 weights are ~= 1. The remaining 1056 ones are summarized as
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 0.06249 0.86890 0.94640 0.90070 0.98330 0.99900 
## Algorithmic parameters: 
##        tuning.chi                bb        tuning.psi        refine.tol 
##         1.548e+00         5.000e-01         4.685e+00         1.000e-07 
##           rel.tol         scale.tol         solve.tol          zero.tol 
##         1.000e-07         1.000e-10         1.000e-07         1.000e-10 
##       eps.outlier             eps.x warn.limit.reject warn.limit.meanrw 
##         8.643e-05         2.910e-09         5.000e-01         5.000e-01 
##      nResample         max.it       best.r.s       k.fast.s          k.max 
##            500             50              2              1            200 
##    maxit.scale      trace.lev            mts     compute.rd fast.s.large.n 
##            200              0           1000              0           2000 
##                   psi           subsampling                   cov 
##            "bisquare"         "nonsingular"         ".vcov.avar1" 
## compute.outlier.stats 
##                  "SM" 
## seed : int(0)

Interpretación del Modelo

  • El modelo utiliza la transformación logarítmica de la variable precio y mantiene las mismas variables predictoras
  • Los coeficientes más significativos son:
    • Goles: +0.111 (p-value < 2e-16). Esto indica que el precio de los jugadores que tienen misma cantidad de asistencias y edad aumenta un 11.7% por cada gol anotado.
    • Asistencias: +0.170 (p-value < 2e-16). Esto indica que el precio de los jugadores que tienen misma cantidad de goles y edad aumenta un 18.5% por cada asistencia adicional.
    • Edad: efecto cuadrático significativo (p-value < 2e-16)
    • América: +0.440 respecto a África (p-value = 0.0008), indica un aumento estadísticamete significativo de 55.2% en el precio de los jugadores americanos con respecto a los africanos. Es mayor con respecto al modelo clásico, con lo cual al quitarle peso a los jugadores mas caros podemos decir que los jugadores americanos son aún mas apreciados que los africanos.
    • Premier League: +0.917 respecto a la liga de referencia, (p-value < 3.66e-16) indica un aumento estadísticamente significativo de 150.2% en el precio de los jugadores de la Premier League con respecto a los jugadores de La Liga (España)
    • Ligue 1 (Francia): -0.231 respecto a la liga de referencia, (p-value = 0.03) indica una disminución estadísticamente significativa de 20.6% en el precio de los jugadores de la Ligue 1 con respecto a los jugadores de La Liga.
    • Bundesliga: -0.282 respecto de la liga de referencia (p-value = 0.004) indica una disminución estadísticamente significativa de 24.6% en el preicio de los jugadores de la Bundesliga con respecto a La Liga.
  • El R² ajustado es 0.4793, similar al modelo anterior pero con mejor interpretabilidad, aumentando un 1.74% con respecto al módelo clásico.

ESTO NO SE SI CORRESPONDE PONERLO PORQUE AL SER UNA FORMA NO PARAMÉTRICA CREO QUE NO CORRESPONDE

Diagnóstico

#Diagnóstico
datos_augmentados <- augment(modelo_multiple_lmrob_2)
datos_augmentados$.std.resid <- datos_augmentados$.resid/(modelo_multiple_lmrob_2$scale*sqrt(1-hatvalues(modelo_multiple_lmrob_2)))
datos_augmentados$.hat <- hatvalues(modelo_multiple_lmrob_2)
g13 <- ggplot(datos_augmentados, aes(.fitted, .resid)) +
  geom_point() +
  geom_hline(yintercept = 0) +
  geom_smooth(se = FALSE) +
  labs(title = "Residuos vs valores predichos") + 
  theme_bw()
g14 <- ggplot(datos_augmentados, aes(sample = .std.resid)) +
  stat_qq() +
  geom_abline() +
  labs(title = "Normal QQ plot") + 
  theme_bw()
g15 <- ggplot(datos_augmentados, aes(.fitted, sqrt(abs(.std.resid)))) +
  geom_point() +
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Scale-location plot")
g16 <- ggplot(datos_augmentados, aes(.hat, .std.resid)) +
  geom_vline(size = 2, colour = "white", xintercept = 0) +
  geom_hline(size = 2, colour = "white", yintercept = 0) +
  geom_point() + 
  geom_smooth(se = FALSE) + 
  theme_bw() +
  labs(title = "Residual vs leverage")
grid.arrange(g13, g14, g15, g16, nrow = 2)

Evaluación y comparación de modelos frente a datos de prueba

modelos_comparacion <- list(multiple_2 = modelo_clasico_multiple,
                robusto_2 = modelo_multiple_lmrob_2)

lista_predicciones_testing = map(.x = modelos_comparacion, .f = augment, newdata = test_data) 


metricas2_test = lista_predicciones_testing$multiple_2 %>%  
  mutate(exp_fitted= exp(.fitted)) %>% 
  metrics(truth=precio, estimate=exp_fitted) %>%
  mutate(.estimate=round(.estimate, 4))

metricas3_test = lista_predicciones_testing$robusto_2 %>% 
  mutate(exp_fitted= exp(.fitted)) %>%
  metrics(truth=precio, estimate=exp_fitted) %>%
  mutate(.estimate=round(.estimate, 4))


metrica <- rbind(metricas2_test[c(1,3),], metricas3_test[c(1,3),])

modelitos_comparacion <- c(rep("Multiple - log(Precio)",2),
               rep("Robusto - log(Precio) - psi = bisquare",2))
metricas <- cbind(modelitos_comparacion, metrica)
kable(metricas)
modelitos_comparacion .metric .estimator .estimate
Multiple - log(Precio) rmse standard 17508456
Multiple - log(Precio) mae standard 7357616
Robusto - log(Precio) - psi = bisquare rmse standard 17592430
Robusto - log(Precio) - psi = bisquare mae standard 7302473
  • El RMSE es menor para el modelo clásico, sin embargo, al estar trabajando con datos que tienen outliers lo correcto es compararlo contra los valores de MAE.
  • El MAE es menor para el modelo robusto, mostrando un mejor desempeño frente al modelo clásico.

Modelos Robustos con otras funciones \(\rho\)

Se aplica la misma fórmula que el modelo anterior pero en lugar de usar la función \(\psi\) por defecto bisqueare hacemos 5 modelos nuevos usando en cada uno diferentes \(\rho\): lqq, welsh, optimal, hampel y ggw.

Evaluación de todos los modelos

modelo_multiple_lmrob_lqq <- lmrob(formula = log(precio) ~ Gls + Age + I(Age^2) + Ast + 
                                   continente + current_club_domestic_competition_id, 
                                 data=train_data,
                           psi = "lqq")

modelo_multiple_lmrob_welsh <- lmrob(formula = log(precio) ~ Gls + Age + I(Age^2) + Ast + 
                                     continente + current_club_domestic_competition_id, 
                                   data=train_data,
                                   psi = "welsh")

modelo_multiple_lmrob_optimal <- lmrob(formula = log(precio) ~ Gls + Age + I(Age^2) + Ast + 
                                       continente + current_club_domestic_competition_id, 
                                     data=train_data,
                                     psi = "optimal")


modelo_multiple_lmrob_hampel <- lmrob(formula = log(precio) ~ Gls + Age + I(Age^2) + Ast + 
                                         continente + current_club_domestic_competition_id, 
                                       data=train_data,
                                       psi = "hampel")

modelo_multiple_lmrob_ggw <- lmrob(formula = log(precio) ~ Gls + Age + I(Age^2) + Ast + 
                                         continente + current_club_domestic_competition_id, 
                                       data=train_data,
                                       psi = "ggw")


modelos <- list(multiple_1 = modelo_clasico_multiple_1, 
                multiple_2 = modelo_clasico_multiple,
                robusto_2 = modelo_multiple_lmrob_2,
                robusto_3 = modelo_multiple_lmrob_lqq,
                robusto_4 = modelo_multiple_lmrob_welsh,
                robusto_5 = modelo_multiple_lmrob_optimal,
                robusto_6 = modelo_multiple_lmrob_hampel,
                robusto_7 = modelo_multiple_lmrob_ggw)

lista_predicciones_testing = map(.x = modelos, .f = augment, newdata = test_data) 


metricas1_test = lista_predicciones_testing$multiple_1 %>%  
  metrics(truth=precio, estimate=.fitted) %>%
  mutate(.estimate=round(.estimate, 4))

metricas2_test = lista_predicciones_testing$multiple_2 %>%  
  mutate(exp_fitted= exp(.fitted)) %>% 
  metrics(truth=precio, estimate=exp_fitted) %>%
  mutate(.estimate=round(.estimate, 4))

metricas3_test = lista_predicciones_testing$robusto_2 %>% 
  mutate(exp_fitted= exp(.fitted)) %>%
  metrics(truth=precio, estimate=exp_fitted) %>%
  mutate(.estimate=round(.estimate, 4))

metricas4_test = lista_predicciones_testing$robusto_3 %>%  
  mutate(exp_fitted= exp(.fitted)) %>% 
  metrics(truth=precio, estimate=exp_fitted) %>%
  mutate(.estimate=round(.estimate, 4))

metricas5_test = lista_predicciones_testing$robusto_4 %>%  
  mutate(exp_fitted= exp(.fitted)) %>% 
  metrics(truth=precio, estimate=exp_fitted) %>%
  mutate(.estimate=round(.estimate, 4))

metricas6_test = lista_predicciones_testing$robusto_5 %>%  
  mutate(exp_fitted= exp(.fitted)) %>% 
  metrics(truth=precio, estimate=exp_fitted) %>%
  mutate(.estimate=round(.estimate, 4))

metricas7_test = lista_predicciones_testing$robusto_6 %>%  
  mutate(exp_fitted= exp(.fitted)) %>% 
  metrics(truth=precio, estimate=exp_fitted) %>%
  mutate(.estimate=round(.estimate, 4))

metricas8_test = lista_predicciones_testing$robusto_7 %>%  
  mutate(exp_fitted= exp(.fitted)) %>% 
  metrics(truth=precio, estimate=exp_fitted) %>%
  mutate(.estimate=round(.estimate, 4))



metricas <- rbind(metricas1_test[c(1,3),], metricas2_test[c(1,3),])
metricas <- rbind(metricas, metricas3_test[c(1,3),])
metricas <- rbind(metricas, metricas4_test[c(1,3),])
metricas <- rbind(metricas, metricas5_test[c(1,3),])
metricas <- rbind(metricas, metricas6_test[c(1,3),])
metricas <- rbind(metricas, metricas7_test[c(1,3),])
metricas <- rbind(metricas, metricas8_test[c(1,3),])

modelitos <- c(rep("Multiple - Precio",2),
               rep("Multiple - log(Precio)",2),
               rep("Robusto - log(Precio) - psi = bisqare",2),
               rep("Robusto - log(Precio) - psi = lqq",2),
               rep("Robusto - log(Precio) - psi = welsh",2), 
               rep("Robusto - log(Precio) - psi = optimal",2),
               rep("Robusto - log(Precio) - psi = hampel",2),
               rep("Robusto - log(Precio) - psi = ggw",2))
metricas <- cbind(modelitos, metricas)
kable(metricas)
modelitos .metric .estimator .estimate
Multiple - Precio rmse standard 15450180
Multiple - Precio mae standard 8469906
Multiple - log(Precio) rmse standard 17508456
Multiple - log(Precio) mae standard 7357616
Robusto - log(Precio) - psi = bisqare rmse standard 17592430
Robusto - log(Precio) - psi = bisqare mae standard 7302473
Robusto - log(Precio) - psi = lqq rmse standard 17622569
Robusto - log(Precio) - psi = lqq mae standard 7311621
Robusto - log(Precio) - psi = welsh rmse standard 17606604
Robusto - log(Precio) - psi = welsh mae standard 7307849
Robusto - log(Precio) - psi = optimal rmse standard 17415950
Robusto - log(Precio) - psi = optimal mae standard 7276587
Robusto - log(Precio) - psi = hampel rmse standard 17679420
Robusto - log(Precio) - psi = hampel mae standard 7328352
Robusto - log(Precio) - psi = ggw rmse standard 17629310
Robusto - log(Precio) - psi = ggw mae standard 7313388
  • El MAE de todos los modelos robustos es menor que el de los modelos clásicos
  • El modelo que utiliza la función \(\rho\) optimal tiene le menor MAE, siendo el de mejor desempeño. Esto es posible ya ya que la funcion optimal es apropiada para datos donde los outliers no son muy pronunciados, que parece ser el caso del precio de los jugadores.

Conclusiones

El análisis estadístico revela patrones fundamentales en la valoración de jugadores que desafían varias percepciones tradicionales del mercado. La evidencia empírica demuestra una clara segmentación del mercado europeo, con ineficiencias significativas que crean oportunidades estratégicas.

La elección metodológica de utilizar regresión robusta con transformación logarítmica demostró ser crucial para la validez del análisis. El método MM-estimation implementado en Robustbase, que utiliza por defecto la función bisquare, permitió manejar eficazmente la heterogeneidad inherente al mercado de fichajes, donde las valoraciones extremas son comunes pero no necesariamente outliers estadísticos. La transformación logarítmica no solo mejoró las propiedades estadísticas del modelo, sino que también proporcionó una interpretación más intuitiva en términos de variaciones porcentuales, alineándose naturalmente con la forma en que el mercado evalúa los cambios en el valor de los jugadores. El uso de estimadores robustos reveló que el mercado de fichajes, aunque volátil, mantiene una estructura subyacente que puede ser modelada de manera más precisa cuando se utilizan métodos que equilibran robustez y eficiencia estadística.

El modelo robusto, con un R² ajustado de 47.93%, captura las dinámicas fundamentales del mercado mientras filtra el “ruido” especulativo. La efectividad de la función bisquare sugiere que, contrario a la percepción popular, las valoraciones extremas en el mercado siguen patrones identificables y no son puramente especulativas.

La Premier League mantiene una prima de valoración del 150.2% sobre La Liga, un diferencial que excede significativamente las diferencias en ingresos operativos entre estas ligas. Esta sobrevaloración sistemática sugiere una burbuja estructural en el mercado inglés, particularmente notable en comparación con Bundesliga (-24.6%) y Ligue 1 (-20.6%), donde el talento parece estar sistemáticamente infravalorado.

Una observación particularmente relevante es la mayor valoración de las asistencias (+18.5%) sobre los goles (+11.7%). Esta diferencia refleja una evolución en la comprensión del valor creativo en el fútbol moderno, donde la capacidad de generar oportunidades se premia por encima de la finalización. Este patrón sugiere una sofisticación creciente en la evaluación del talento.

La relación cuadrática con la edad emerge como un factor crítico en la valoración, señalando una ventana óptima de valoración que el mercado reconoce consistentemente. Este patrón tiene implicaciones profundas para la gestión de activos deportivos y la planificación de plantillas a largo plazo.

La brecha de valoración del 55.2% entre jugadores americanos y africanos, controlando por rendimiento y liga, revela un sesgo de mercado significativo. Esta disparidad, estadísticamente significativa (p-value = 0.0008), indica una ineficiencia de mercado estructural que trasciende el rendimiento deportivo puro.

Las ineficiencias identificadas en el mercado sugieren que el valor real de un jugador puede diferir significativamente de las valoraciones de mercado actuales, especialmente en ligas secundarias y mercados emergentes. La transformación logarítmica del modelo revela que estas discrepancias siguen patrones predecibles y explotables.

El análisis también señala una evolución en la estructura del mercado, donde los factores tradicionales de valoración están siendo complementados por métricas más sofisticadas. La significativa prima por creatividad sugiere un mercado que está comenzando a valorar más acertadamente las contribuciones tácticas complejas.

Estos hallazgos indican que el mercado de transferencias, aunque cada vez más sofisticado, mantiene ineficiencias estructurales significativas. La combinación de sesgos geográficos, primas de liga y valoración de habilidades específicas crea un panorama complejo pero analíticamente navegable para la identificación de valor.

Bibliografía

LS0tCnRpdGxlOiAiUHJlZGljY2nDs24gZGVsIHByZWNpbyBkZSBsb3MganVnYWRvcmVzIGVuIGxhcyA1IGxpZ2FzIGRlIGZ1dGJvbCBldXJvcGVhcyBtYXMgaW1wb3J0YW50ZXMiCmF1dGhvcjogIlJvZHJpZ28gTWFycXVlcywgQWd1c3TDrW4gQ2VwZWRhLCBHZXJtw6FuIEZlcm7DoW5kZXoiCmRhdGU6ICIxNSBkZSBEaWNpZW1icmUgZGUgMjAyNCIKc3VidGl0bGU6ICJFbmZvcXVlIEVzdGFkw61zdGljbyBkZWwgQXByZW5kaXphamUiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdGhlbWU6IHVuaXRlZAogICAgY29kZV9kb3dubG9hZDogeWVzCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KZGl2Lm1haW4tY29udGFpbmVyIHsKICBtYXgtd2lkdGg6IDE2MDBweDsKICBtYXJnaW4tbGVmdDogYXV0bzsKICBtYXJnaW4tcmlnaHQ6IGF1dG87Cn0KPC9zdHlsZT4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojTGlicmVyaWFzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRpZHltb2RlbHMpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoTUFTUykKbGlicmFyeShyb2J1c3RiYXNlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGNvcnJwbG90KQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmBgYAoKIyBPYmpldGl2bwoKRWwgZsO6dGJvbCBlcyBlbCBkZXBvcnRlIG3DoXMgcG9wdWxhciwgZWwgcXVlIHRpZW5lIHVuIG1lcmNhZG8gbcOhcyBncmFuZGUgeSBlbiBlbCBjdWFsIGVsIHZhbG9yIGRlIGxhcyB0cmFuc2ZlcmVuY2lhcyBkZSBsb3MganVnYWRvcmVzIGVzIGVsIG3DoXMgYWx0by4gRW4gZXNlIHNlbnRpZG8sIG5vcyBwcm9wb25lbW9zIGFuYWxpemFyIGxhcyB0cmFuc2ZlcmVuY2lhcyB5IGNvdGl6YWNpb25lcyBkZSBsb3MganVnYWRvcmVzIGRlIGxhIGxpZ2EgaW5nbGVzYSwgdW5hIGRlIGxhcyBtZWpvcmVzIGxpZ2FzIGRlbCBtdW5kby4KClBvciBlc3RlIG1vdGl2bywgeSBoYWNpZW5kbyB1c28gZGUgbG9zIGNvbm9jaW1pZW50b3MgYXBvcnRhZG9zIHBvciBsYSBtYXRlcmlhLCBvcHRhbW9zIHBvciBwcm9mdW5kaXphciBlbiBlbCBkZXNhcnJvbGxvIGRlIG3DqXRvZG9zIGRlIHJlZ3Jlc2nDs24gcm9idXN0b3MgcGFyYSBoYWNlciBhbsOhbGlzaXMgcHJlZGljdGl2b3MsIGhhY2llbmRvIGZvY28gZW4gbGEgZXhwbGljYWJpbGlkYWQgZGVsIG1vZGVsbyBwYXJhIHByZWRlY2lyIGxhcyB0cmFuc2ZlcmVuY2lhcyBkZSBsb3MganVnYWRvcmVzLgoKRWwgb2JqZXRpdm8gZGVsIHByZXNlbnRlIHRyYWJham8gY29uc2lzdGUgZW4gbGEgdXRpbGl6YWNpw7NuIGRpc3RpbnRvcyBtw6l0b2RvcyByb2J1c3RvcyBwYXJhIHByZWRlY2lyIGxhIHZhcmlhYmxlIG9iamV0aXZvOiBlbCBwcmVjaW8gZGUgdHJhbnNmZXJlbmNpYSBkZSBsb3MganVnYWRvcmVzLgoKLSBPYnNlcnZhciBzaSBlbCBwYcOtcyBkZSBvcmlnZW4gZXMgdW5hIHZhcmlhYmxlIGV4cGxpY2F0aXZhIGNvbiByZXNwZWN0byBhIGxhIHZhcmlhYmxlIG9iamV0aXZvLgotIElkZW50aWZpY2FyIHNpIGxvcyByYXRpb3MgcXVlIGRlbXVlc3RyYW4gZWwgZGVzZW1wZcOxbyBkZSBsb3MganVnYWRvcmVzIGdlbmVyYW4gaW1wYWN0byBzb2JyZSBsYSB0cmFuc2ZlcmVuY2lhIGRlIGxvcyBqdWdhZG9yZXMuCi0gVXRpbGl6YXIgbGEgcmVncmVzacOzbiByZWdyZXNpw7NuIGRlIG3DrW5pbW9zIGN1YWRyYWRvcyBwb25kZXJhZG9zLCByZWdyZXNpw7NuIGRlIG3DrW5pbW9zIGN1YWRyYWRvcyByb2J1c3RvcyAoSHViZXIpLgotIENvbXBhcmFyIGVsIGRlc2VtcGXDsW8gZGUgbG9zIGRpc3RpbnRvcyBtb2RlbG9zIHBhcmEgcHJlZGVjaXIgbGEgdmFyaWFibGUgb2JqZXRpdm8uCgoKIyBJbnRyb2R1Y2Npw7NuCgpFbiBlbCBtw6l0b2RvIGxpbmVhbCBjbMOhc2ljbyBzZSB1dGlsaXphIGVsIG3DqXRvZG8gZGUgY3VhZHJhZG9zIG3DrW5pbW9zIHBhcmEgZW5jb250cmFyIGxvcyBwYXLDoW1ldHJvcyAkXGJldGEkLgoKTGEgZnVuY2nDs24gZGUgcMOpcmRpZGEgcXVlIHNlIHF1aWVyZSBtaW5pbWl6YXIgZXMgbGEgc3VtYSBkZWwgY3VhZHJhZG8gZGUgbG9zIHJlc2lkdW9zLgoKJCQKZyhhLCBiKSA9IFxzdW1fe2k9MX1ebiBcbGVmdCggWV9pIC0gXGxlZnQoIGEgKyBiIFhfaSBccmlnaHQpIFxyaWdodCleMiAgXHF1YWQgXHRleHR7KDEpfQokJAoKRW4gbG9zIG1vZGVsb3MgbGluZWFsZXMgcm9idXN0b3MgcXVlcmVtb3MgY2FtYmlhciBsYSBmdW5jacOzbiBkZSBwZXJkaWRhIHRhbCBxdWU6CgotIFNlYSBJbnNlbnNpYmxlIGEgdmFsb3JlcyBleHRyZW1hZGFtZW50ZSBncmFuZGVzIG8gb3V0bGllcnMobyByZXNpZHVvcyBncmFuZGVzKQotIENyZXpjYSBtZW5vcyBxdWUgbcOtbmltb3MgY3VhZHJhZG9zIGN1YW5kbyBtaXJhbW9zIGxvIHN1ZmljaWVudGVtZW50ZSBsZWpvcyBkZWwgY2VybwotIFRlbmdhIEFsdGEgZWZpY2llbmNpYTogc2kgbG9zIGRhdG9zIGRlIGxhIG11ZXN0cmEgc2lndWllcmFuIGVsIG1vZGVsbyBkZSByZWdyZXNpw7NuIGNvbiBlcnJvcmVzIG5vcm1hbGVzLCBxdWVyZW1vcyBxdWUgZWwgZXN0aW1hZG9yIChiZXRhKSBxdWUgZWwgbcOpdG9kbyByb2J1c3RhIGNhbGN1bGEgc2UgcGFyZXpjYSBhbCBkZSBtw61uaW1vcyBjdWFkcmFkb3MsIHBvciBsbyBxdWUgbGEgZnVuY2nDs24gZGUgcMOpcmRpZGEgcXVlIGVsIG3DqXRvZG8gcm9idXN0byBjYWxjdWxhIGRlYmVyw61hIHBhcmVjZXJzZSBhbCBkZSBtw61uaW1vcyBjdWFkcmFkby4KCgokJApnKGEsIGIpID0gXHN1bV97aT0xfV5uIFxyaG8gXGxlZnQoIFxmcmFje1lfaSAtIFxsZWZ0KCBhICsgYiBYX2kgXHJpZ2h0KX17c19ufSBccmlnaHQpICAgXHF1YWQgXHRleHR7KDIpfSAKJCQKRGVmaW5pY2nDs24gZGUgJFxyaG8kIAoKLSRccmhvJCA6IFIg4oaSUi4KClVuYSBmdW5jacOzbi0kXHJobyQgZGVub3RhcsOhIGEgdW5hIGZ1bmNpw7NuICRccmhvJCh1KSB0YWwgcXVlOgoKLSRccmhvJCgwKSA9IDAuCgotJFxyaG8kKOKIknUpID0gJFxyaG8kKHUpLgoKLTAg4omkIHUg4omkIHYgaW1wbGljYSAkXHJobyQodSkg4omkICRccmhvJCh2KS4KCi0kXHJobyQgZXMgY29udGludWEuCgotc3VwfnUgJFxyaG8kKHUpID0gMS4KClNpICRccmhvJCh1KSA8IDEgeSAwIOKJpCB1IDwgdiBlbnRvbmNlcyAkXHJobyQodSkgPCAkXHJobyQodikuCgplcyB1bmEgZnVuY2nDs24gYWNvdGFkYSwgY3JlY2llbnRlIHkgc2ltw6l0cmljYSBhbHJlZGVkb3IgZGVsIGNlcm8sIHkgJHNfbiQgZXMgdW4gZXN0aW1hZG9yIGRlIGVzY2FsYSBxdWUganVlZ2EgZWwgcGFwZWwgZGUgz4MgZW4gZWwgbW9kZWxvIGNsw6FzaWNvIGRlIHJlZ3Jlc2nDs24uCgpVbmEgcG9zaWJpbGlkYWQgZXMgYWp1c3RhciB1bmEgcmVjdGEgdXNhbmRvIHVuIHByb2NlZGltaWVudG8gZGUgYWp1c3RlIHJvYnVzdG8sIHBvciBlamVtcGxvIHVuIE1NLWVzdGltYWRvciBkZSByZWdyZXNpw7NuLCBwcm9wdWVzdG8gcG9yIFlvaGFpIFsxOTg3XS4gRW4gUiwgZXN0byBlc3TDoSBwcm9ncmFtYWRvIGRlbnRybyBkZSBsYSBydXRpbmEgbG1yb2IgZW4gZWwgcGFxdWV0ZSByb2J1c3RiYXNlIGRlIFIuIExhIGVzdGltYWNpw7NuIHNlIGhhY2UgZW4gdHJlcyBldGFwYXMsIHNlIHByb3BvbmUgdW4gZXN0aW1hZG9yIGluaWNpYWwgZGUgbG9zIHBhcsOhbWV0cm9zLCBhIHBhcnRpciBkZSDDqWwgc2UgZXN0aW1hIGEgc24geSBtYW51YWxtZW50ZSBzZSBvYnRpZW5lbiBsb3MgZXN0aW1hZG9yZXMgZGUgbG9zIHBhcsOhbWV0cm9zIGEgcGFydGlyIGRlIGVsbG9zLCBtaW5pbWl6YW5kbyBsYSBmdW5jacOzbiBvYmpldGl2bwoKRXhpc3RlbiB2YXJpYXMgZnVuY2lvbmVzICRccmhvJCBxdWUgc2Vyw6FuIGV2YWx1YWRhcyBlbiBlbCBwcmVzZW50ZSB0cmFiYWpvLiBQb3IgZGVmZWN0bywgcm9idXN0YmFzZSB1dGlsaXphIGxhIGJpY3VhZHJhZGEgKGJpc3F1YXJlKSwgcGVybyB0YW1iacOpbiBzZSBwdWVkZW4gaW1wbGVtZW50YXIgbHFxLCB3ZWxzaCwgb3B0aW1hbCwgZXRjLgoKUm9idXN0YmFzZSB1dGlsaXphIGVsIGFsZ29yaXRtbyBJdGVyYXRpdmVseSBSZXdlaWdodGVkIExlYXN0IFNxdWFyZXMgKElSV0xTKSBwYXJhIGVzdGltYXIgbG9zIHBhcsOhbWV0cm9zICRcYmV0YSQuIEVsIHByb2Nlc28gY29uc2lzdGUgZW4gbGEgc2lndWllbnRlcyBldGFwYXM6CgoxLiBJbmljaWFsaXphciBlbCBwcm9jZXNvIHV0aWxpemFuZG8gdW4gZXN0aW1hY2nDs24gaW5pY2lhbCBkZSBhIHkgYiB1dGlsaXphbmRvIGVsIG3DqXRvZG8gZGUgY3VhZHJhZG9zIG3DrW5pbW9zLgoKMi4gRGVyaXZhciBsYSBmdW5jacOzbiAoMikgcmVzcGVjdG8gYSBsb3MgcGFyw6FtZXRyb3MgYSB5IGI6CiQkClxmcmFje1xwYXJ0aWFsIGd9e1xwYXJ0aWFsIGF9ID0gXHN1bV97aT0xfV57bn0gXHBzaSBcbGVmdChcZnJhY3tZX2kgLSAoYSArIGJYX2kpfXtzX259IFxyaWdodCkgXGNkb3QgXGZyYWN7LTF9e3Nfbn0gXHF1YWQgXHRleHR7KDMpfQokJAokJApcZnJhY3tccGFydGlhbCBnfXtccGFydGlhbCBifSA9IFxzdW1fe2k9MX1ee259IFxwc2kgXGxlZnQoXGZyYWN7WV9pIC0gKGEgKyBiWF9pKX17c19ufSBccmlnaHQpIFxjZG90IFxmcmFjey1YX2l9e3Nfbn0gXHF1YWQgXHRleHR7KDQpfQokJAoKZG9uZGUgCgokJApcZnJhY3tkIFxyaG8oeCl9e2R4fSA9IFxwc2koeCkgXHF1YWQgXHRleHR7KDUpfQokJAoKMy4gU2UgY2FsY3VsYW4gbG9zIHBlc29zIGRlIGNhZGEgb2JzZXJ2YWNpb24KCiQkCndfaSA9IFxmcmFje1xwc2kgXGxlZnQoXGZyYWN7WV9pIC0gKGEgKyBiWF9pKX17c30gXHJpZ2h0KX17XGZyYWN7WV9pIC0gKGEgKyBiWF9pKX17c319IFxxdWFkIFx0ZXh0eyg2KX0KJCQKCjQuIFNlIG11bHRpcGxpY2EgYSBjYWRhIG9ic2VydmFjacOzbiBwb3Igc3UgcmVzcGVjdGl2byBwZXNvLgoKNS4gU2UgdnVlbHZlbiBhIGVzdGltYXIgbG9zIHBhcsOhbWV0cm9zIGEgeSBiIGNvbiB1bmEgcmVncmVzacOzbiBwb25kZXJhZGEuCgpTZSByZXBpdGVuIGxvcyBwYXNlcyBoYXN0YSBubyBvYnNlcnZhciBtYXMgbWVqb3JhcyBvIGhhc3RhIHVuIG3DoXhpbW8gZGUgaXRlcmFjaW9uZXMuCgpBIGNvbnRpbnVhY2nDs24gZGUgbXVlc3RyYXMgTGFzIGZ1bmNpb25lcyAkXHJobyQgbcOhcyBjb211bmVzIHkgY29tbyBzZSBjb21wb3J0YW4gbG9zIHBlc29zIGRlIGxhcyBtaXNtYXMuCgoxLiBCaXNxdWFyZSAoVHVrZXkpOgokJApccmhvKHgpID0gXGJlZ2lue2Nhc2VzfQoxIC0gKDEgLSAoeC9rKV4yKV4zICYgXHRleHR7c2kgfSB8eHwgXGxlcSBrIFxcCjEgJiBcdGV4dHtzaSB9IHx4fCA+IGsKXGVuZHtjYXNlc30gXHF1YWQgXHRleHR7KDcpfQokJApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBzZXQgbWFyZ2lucyBmb3IgcGxvdHMKb3B0aW9ucyhTd2VhdmVIb29rcz1saXN0KGZpZz1mdW5jdGlvbigpIHBhcihtYXI9YygzLDMsMS40LDAuNyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWdwPWMoMS41LCAwLjUsIDApKSkpCiMjIHggYXhpcyBmb3IgcGxvdHM6CnguIDwtIHNlcSgtNSwgMTAsIGxlbmd0aC5vdXQgPSAxNTAxKQpzb3VyY2Uoc3lzdGVtLmZpbGUoInh0cmFSL3Bsb3QtcHNpRnVuLlIiLCBwYWNrYWdlID0gInJvYnVzdGJhc2UiLCBtdXN0V29yaz1UUlVFKSkKZ2V0T3B0aW9uKCJTd2VhdmVIb29rcyIpW1siZmlnIl1dKCkKcC5wc2lGdW4oeC4sICJiaXdlaWdodCIsIHBhciA9IDQuNjg1KQpgYGAKCjIuIEh1YmVyOgokJApccmhvKHgpID0gXGJlZ2lue2Nhc2VzfQpcZnJhY3t4XjJ9ezJ9ICYgXHRleHR7c2kgfSB8eHwgXGxlcSBrIFxcCmt8eHwgLSBcZnJhY3trXjJ9ezJ9ICYgXHRleHR7c2kgfSB8eHwgPiBrClxlbmR7Y2FzZXN9IFxxdWFkIFx0ZXh0eyg4KX0KJCQKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZXRPcHRpb24oIlN3ZWF2ZUhvb2tzIilbWyJmaWciXV0oKQpwbG90KGh1YmVyUHNpLCB4LiwgeWxpbT1jKC0xLjQsIDUpLCBsZWcubG9jPSJ0b3ByaWdodCIsIG1haW49RkFMU0UpCmBgYAoKMy4gV2Vsc2g6CiQkClxyaG8oeCkgPSAxIC0gXGV4cCgteF4yL2teMikgXHF1YWQgXHRleHR7KDkpfQokJAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdldE9wdGlvbigiU3dlYXZlSG9va3MiKVtbImZpZyJdXSgpCnAucHNpRnVuKHguLCAiV2Vsc2giLCBwYXIgPSAyLjExKQpgYGAKCjQuIEhhbXBlbDoKJCQKXHJobyh4KSA9IFxiZWdpbntjYXNlc30KXGZyYWN7eF4yfXsyfSAmIFx0ZXh0e3NpIH0gfHh8IFxsZXEgYSBcXAphfHh8IC0gXGZyYWN7YV4yfXsyfSAmIFx0ZXh0e3NpIH0gYSA8IHx4fCBcbGVxIGIgXFwKXGZyYWN7YShjfHh8IC0geF4yLzIgLSBiYyArIGJeMi8yKX17Yy1ifSAmIFx0ZXh0e3NpIH0gYiA8IHx4fCBcbGVxIGMgXFwKYShjIC0gXGZyYWN7Yn17Mn0pICYgXHRleHR7c2kgfSB8eHwgPiBjClxlbmR7Y2FzZXN9IFxxdWFkIFx0ZXh0eygxMCl9CiQkCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2V0T3B0aW9uKCJTd2VhdmVIb29rcyIpW1siZmlnIl1dKCkKIyMgc2VlIGFsc28gaGFtcGVsUHNpCnAucHNpRnVuKHguLCAiSGFtcGVsIiwgcGFyID0gIyMgRGVmYXVsdCwgYnV0IHJvdW5kZWQ6CiAgICAgICAgICAgcm91bmQoYygxLjUsIDMuNSwgOCkgKiAwLjkwMTYwODUsIDEpKQpgYGAKCjUuIExRUSAoTGluZWFybHkgUXVhZHJhdGljYWxseSBRdWFkcmF0aWMpOgokJApccmhvKHgpID0gXGJlZ2lue2Nhc2VzfQpcZnJhY3t4XjJ9ezJ9ICYgXHRleHR7c2kgfSB8eHwgXGxlcSBjXzEgXFwKY18xfHh8IC0gXGZyYWN7Y18xXjJ9ezJ9ICYgXHRleHR7c2kgfSBjXzEgPCB8eHwgXGxlcSBjXzIgXFwKXGZyYWN7KGNfM3x4fCAtIHheMi8yKX17Y18zLWNfMn0gJiBcdGV4dHtzaSB9IGNfMiA8IHx4fCBcbGVxIGNfMyBcXApjXzMgJiBcdGV4dHtzaSB9IHx4fCA+IGNfMwpcZW5ke2Nhc2VzfSBccXVhZCBcdGV4dHsoMTEpfQokJAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdldE9wdGlvbigiU3dlYXZlSG9va3MiKVtbImZpZyJdXSgpCnAucHNpRnVuKHguLCAiTFFRIiwgcGFyID0gYygtLjUsMS41LC45NSxOQSkpCmBgYAoKNi4gT3B0aW1hbAokJApccHNpKHgpID0gXGJlZ2lue2Nhc2VzfSAKeCAmIFx0ZXh0e3NpIH0gfHh8IFxsZXEgYSBcXAphIFxjZG90IFx0ZXh0e3NpZ259KHgpIFxjZG90IFxsZWZ0KDEgLSBcbGVmdChcZnJhY3t8eHwgLSBhfXtiIC0gYX1ccmlnaHQpXjJccmlnaHQpXjIgJiBcdGV4dHtzaSB9IGIgPCB8eHwgXGxlcSBjIFxcCjAgJiBcdGV4dHtzaSB9IHx4fCA+IGMgClxlbmR7Y2FzZXN9IFxxdWFkIFx0ZXh0eygxMil9CiQkCgoKRG9uZGUgJGskLCAkYSQsICRiJCwgJGMkLCAkY18xJCwgJGNfMiQgeSAkY18zJCBzb24gY29uc3RhbnRlcyBxdWUgZGV0ZXJtaW5hbiBsb3MgcHVudG9zIGRlIHF1aWVicmUgeSBsYSBmb3JtYSBkZSBjYWRhIGZ1bmNpw7NuLgoKRXhpc3RlbiBtdWNoYXMgb3RyYXMgcHJvcHVlc3RhcyBkZSBlc3RpbWFkb3JlcyByb2J1c3RvcyBwYXJhIHJlZ3Jlc2nDs24sIHBvciBlamVtcGxvIExNUyAobGVhc3QgbWVkaWFuIG9mIHNxdWFyZXMpLCBMVFMgKGxlYXN0IHRyaW1tZWQgc3F1YXJlcyksIM+E4oiSZXN0aW1hZG9yZXMgZGUgcmVncmVzacOzbiwgeSBjYXNpIHRvZGFzIGVzdMOhbiBpbXBsZW1lbnRhZGFzIGVuIFIuCgoKIyBEYXRhc2V0CgpFbCBzZXQgZGUgZGF0b3MgY29uc2lzdGUgZW4gZG9zIGJhc2VzLCBhbWJhcyBvYnRlbmlkYXMgZGUgS2FnZ2xlLgoKTGEgYmFzZSBkZSBkYXRvcyBjb24gbG9zIHByZWNpb3MgZGUgbG9zIGp1Z2Fkb3JlcyBzZSBvYnR1dm8gZGUgbGEgd2ViIFRyYW5zZmVybWFya3QuIEN1ZW50YSBjb24gMzI0MDUgcmVnaXN0cm9zLCB5IGRvcyBkZSBzdXMgdmFyaWFibGVzIHNvbiBsb3MgcHJlY2lvcyBhY3R1YWxlcyBkZSBsb3MganVnYWRvcmVzIHkgZWwgcHJlY2lvIG1hcyBhbHRhIGFsY2FuemFkby4gQWRlbcOhcyBjdWVudGEgY29uIGxhIGluZm9ybWFjacOzbiBkZWwgY2x1YiB5IGxpZ2EgYWN0dWFsIGRlIGNhZGEganVnYWRvci4KCkxhcyBvdHJhIGJhc2UgZGUgZGF0b3MgY3VlbnRhIGNvbiBsYXMgbcOpdHJpY2FzIGR1cmFudGUgbGEgdGVtcG9yYWRhIDIwMjMvMjAyMyBkZSBsb3MganVnYWRvcmVzIGRlIGxhcyA1IGxpZ2FzIG1hcyBpbXBvcnRhbnRlcyBkZWwgZsO6dGJvbCBldXJvcGVvLiBBbGd1bmFzIGRlIHN1cyB2YXJpYWJsZXMgc29uIEVkYWQsIGdvbGVzIGFub3RhZG9zLCBhc2lzdGVuY2lhcywgcG9zaWNpw7NuIGRlbnRybyBkZWwgY2FtcG8gZGUganVlZ28sIG5hY2lvbmFsaWRhZCwgZW50cmUgb3RyYXMuCgpBbWJhcyBiYXNlcyBkZSBkYXRvcyBzZSB1bmllcm9uIHBvciBlbCBub21icmUgeSBhcGVsbGlkbyBkZWwganVnYWRvciwgeSBlbCBjbHViIGFjdHVhbC4gQWRpY2lvbmFsbWVudGUsIHNlIGFncmVnw7MgbGEgdmFyaWFibGUgQ29udGluZW50ZSBhbCBkYXRhc2V0IGZpbmFsLCBsYSBjdWFsIGluZm9ybWEgZWwgY29udGluZW50ZSBuYXRhbCBkZSBjYWRhIGp1Z2Fkb3IuCgpFbCBkYXRhc2V0IHNlIGRpdmlkZSBlbiBFbnRyZW5hbWllbnRvIHkgUHJ1ZWJhLCBkZSBtYW5lcmEgZXN0cmF0aWZpY2FkYSBzZWfDum4gZWwgcHJlY2lvLiBTZSB1dGlsaXphIGVsIHNpZ3VpZW50ZSBib3hwbG90IHBhcmEgZ2VuZXJhciB1bmEgbnVldmEgY2F0ZWdvcsOtYSBxdWUgY2F0ZWdvcmljZSBzZWfDum4gZWwgcHJlY2lvIGRlbCBqdWdhZG9yLgoKTHVlZ28gdXRpbGl6YXJlbW9zIGVzdGEgbnVldmEgdmFyaWFibGUgcGFyYSByZWFsaXphciBsYSBkaXZpc2nDs24gZGVsIGRhdGFzZXQgZGUgbWFuZXJhIGVzdHJhdGlmaWNhZGEuCgojIyBDYXRlZ29yw61hcyBkZSBsYSBudWV2YSB2YXJpYWJsZQoKLSBNdXkgYmFqbzogTWVub3IgYSBtZWRpYW5hIChRMikKLSBCYWpvOiBlbnRyZSBtZWRpYW5hIChRMikgeSBjdWFydGlsIHN1cGVyaW9yIChRMykKLSBNZWRpbzogZW50cmUgY3VhcnRpbCBzdXBlcmlvciAoUTMpIHkgbMOtbWl0ZSBzdXBlcmlvciAoUTMgKyAxLjUgeCBJUVIpCi0gQWx0bzogZW50cmUgbGltaXRlIHN1cGVyaW9yIHkg4oKsMTAwLjAwMC4wMDAKLSBNdXkgQWx0bzogbWF5b3IgYSDigqwxMDAuMDAwLjAwMCAocmVwcmVzZW50YSBsb3MgMTAganVnYWRvcmVzIG1hcyBjYXJvcykKCkVsIG9iamV0aXZvIGVzIHF1ZSBxdWVkYW4gZXN0cmF0aWZpY2Fkb3MgbG9zIGp1Z2Fkb3JlcyBtYXMgY2Fyb3MgdSBvdXRsaWVycywgcG9yIGVzbyBubyBzZSByZWFsaXphIHVuYSBkaXZpc2nDs24gcG9yIGRlYmFqbyBkZSBRMi4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgQ2FyZ2EgZGUgZGF0c2V0CmRmIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5jc3YoImRhdGFzZXQuY3N2IikpCmRmIDwtIGFzLmRhdGEuZnJhbWUoZGYpCmRmIDwtIG5hLm9taXQoZGYpCmNvbG5hbWVzKGRmKVtjb2xuYW1lcyhkZikgPT0gIm1hcmtldF92YWx1ZV9pbl9ldXIiXSA8LSAicHJlY2lvIgoKIyBBZ3JlZ2FyIG51ZXZhIHZhcmlhYmxlIHF1ZSB0ZW5nYSBlbiBjdWVudGEgbG9zIHByZWNpb3MgZGUgbG9zIGp1Z2Fkb3JlcyB5IHBvZGVyIGhhY2VyIHVuIHNwbGl0IAojIHRlc3QtdHJhaW4gZXN0cmF0aWZpY2FkbwpjYWphX3ByZWNpb3MgPC0gYm94cGxvdChkZiRwcmVjaW8pCgpkZiRwcmVjaW9fY2F0IDwtIE5BCgpmb3IgKGkgaW4gMTpsZW5ndGgoZGYkcHJlY2lvKSkgewogIGlmIChkZiRwcmVjaW9baV0gPj0gMTAwMDAwMDAwKSB7IAogICAgZGYkcHJlY2lvX2NhdFtpXSA8LSAibXV5X2FsdG8ifQogIGlmIChkZiRwcmVjaW9baV0gPCAxMDAwMDAwMDAgJiYgZGYkcHJlY2lvW2ldID49IGNhamFfcHJlY2lvcyRzdGF0c1s1XSkgewogICAgZGYkcHJlY2lvX2NhdFtpXSA8LSAiYWx0byJ9CiAgaWYgKGRmJHByZWNpb1tpXSA8IGNhamFfcHJlY2lvcyRzdGF0c1s1XSAmJiBkZiRwcmVjaW9baV0gPj0gY2FqYV9wcmVjaW9zJHN0YXRzWzRdKSB7CiAgICBkZiRwcmVjaW9fY2F0W2ldIDwtICJtZWRpbyJ9CiAgaWYgKGRmJHByZWNpb1tpXSA8IGNhamFfcHJlY2lvcyRzdGF0c1s0XSAmJiBkZiRwcmVjaW9baV0gPj0gY2FqYV9wcmVjaW9zJHN0YXRzWzNdKSB7CiAgICBkZiRwcmVjaW9fY2F0W2ldIDwtICJiYWpvIn0KICBpZiAoZGYkcHJlY2lvW2ldIDwgY2FqYV9wcmVjaW9zJHN0YXRzWzNdKXsKICAgIGRmJHByZWNpb19jYXRbaV0gPC0gIm11eV9iYWpvIn0KfQoKIyBTdXBvbmllbmRvIHF1ZSBgZGYkY2xhc2VgIGVzIGxhIHZhcmlhYmxlIGNhdGVnw7NyaWNhCnNldC5zZWVkKDI4NzQ5NjU4KSAgIyBGaWphciBzZW1pbGxhIHBhcmEgcmVwcm9kdWNpYmlsaWRhZAoKIyBDcmVhciDDrW5kaWNlcyBlc3RyYXRpZmljYWRvcyBiYXNhZG9zIGVuIGxhIHZhcmlhYmxlIGBjbGFzZWAKdHJhaW5faW5kaWNlcyA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGRmJHByZWNpb19jYXQsIHAgPSAwLjcsIGxpc3QgPSBGQUxTRSkgICMgNzAlIGVudHJlbmFtaWVudG8KCiMgRGl2aWRpciBsb3MgZGF0b3MKdHJhaW5fZGF0YSA8LSBkZlt0cmFpbl9pbmRpY2VzLCBdCnRlc3RfZGF0YSA8LSBkZlstdHJhaW5faW5kaWNlcywgXQpgYGAKCiMjIyBWZXJpZmljYXIgbGEgZGlzdHJpYnVjacOzbiBkZSBjbGFzZXMKCiMjIyMgRGF0b3MgZGUgRW50cmVuYW1pZW50bwpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KdGFibGUodHJhaW5fZGF0YSRwcmVjaW9fY2F0KQpgYGAKIyMjIyBEYXRvcyBkZSBwcnVlYmEKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnRhYmxlKHRlc3RfZGF0YSRwcmVjaW9fY2F0KQpgYGAKCiMgQW7DoWxpc2lzIEV4cGxvcmF0b3JpbyBkZSBEYXRvcwoKIyMgR3LDoWZpY29zIGRlIGJhcnJhcyBkZSBhbGd1bmFzIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjb2xuYW1lcyhkZilbY29sbmFtZXMoZGYpID09ICJtYXJrZXRfdmFsdWVfaW5fZXVyIl0gPC0gInByZWNpbyIKZGYkY29udGluZW50ZSA8LSBmYWN0b3IoZGYkY29udGluZW50ZSwgbGV2ZWxzID0gYygiZXVyb3BhIiwgImFtZXJpY2EiLCAiYWZyaWNhIiwgImFzaWFfb2NlYW5pYSIpKQpncmFmaWNvMSA8LSBnZ3Bsb3QoZGYsIGFlcyh4PWNvbnRpbmVudGUsIGZpbGwgPSBjb250aW5lbnRlKSkgKwogIGdlb21fYmFyKCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZSA9IFRSVUUsIG9wdGlvbiA9ICJEIikgKyAjIFBhbGV0YSBhY2Nlc2libGUKICBsYWJzKHkgPSAiQ2FudGlkYWQiLCAKICAgICAgIHggPSAiQ29udGluZW50ZSIsCiAgICAgICB0aXRsZSA9ICJDYW50aWRhZCBkZSBqdWdhZG9yZXMgcG9yIGNvbnRpbmVudGUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgdmp1c3Q9MSwgaGp1c3Q9MSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKZGYkQ29tcCA8LSBmYWN0b3IoZGYkQ29tcCwgbGV2ZWxzID0gYygiZXMgTGEgTGlnYSIsICJpdCBTZXJpZSBBIiwgImZyIExpZ3VlIDEiLCAiZGUgQnVuZGVzbGlnYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImVuZyBQcmVtaWVyIExlYWd1ZSIpKQpncmFmaWNvMiA8LSBnZ3Bsb3QoZGYsIGFlcyh4PUNvbXAsIGZpbGwgPSBDb21wKSkgKyAKICBnZW9tX2JhcigpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFLCBvcHRpb24gPSAiRCIpICsgIyBQYWxldGEgYWNjZXNpYmxlCiAgbGFicyh5ID0gIkNhbnRpZGFkIiwgeCA9ICJMaWdhIiwgdGl0bGUgPSAiQ2FudGlkYWQgZGUganVnYWRvcmVzIHBvciBsaWdhIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCB2anVzdD0xLCBoanVzdD0xKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwb3NpY2lvbmVzIDwtIHRhYmxlKGRmJHBvc2l0aW9uKQpwb3NpY2lvbmVzIDwtIG5hbWVzKHNvcnQocG9zaWNpb25lcykpCmRmJHBvc2l0aW9uIDwtIGZhY3RvcihkZiRwb3NpdGlvbiwgbGV2ZWxzID0gcG9zaWNpb25lcykKZ3JhZmljbzMgPC0gZ2dwbG90KGRmLCBhZXMoeD1wb3NpdGlvbiwgZmlsbCA9IHBvc2l0aW9uKSkgKyAKICBnZW9tX2JhcigpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFLCBvcHRpb24gPSAiRCIpICsgIyBQYWxldGEgYWNjZXNpYmxlCiAgbGFicyh5ID0gIkNvdW50IiwgeCA9ICJMaWdhIiwgdGl0bGUgPSAiQ2FudGlkYWQgZGUganVnYWRvcmVzIHBvciBwb3NpY2lvbiIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgdmp1c3Q9MSwgaGp1c3Q9MSwpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmdyYWZpY280IDwtIGdncGxvdChkZiwgYWVzKHg9Zm9vdCwgZmlsbCA9IGZvb3QpKSArIAogIGdlb21fYmFyKCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZSA9IFRSVUUsIG9wdGlvbiA9ICJEIikgKyAjIFBhbGV0YSBhY2Nlc2libGUKICBsYWJzKHkgPSAiQ291bnQiLCB4ID0gIkxpZ2EiLCB0aXRsZSA9ICJDYW50aWRhZCBkZSBqdWdhZG9yZXMgcG9yIHBpZSBoYWJpbCIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgdmp1c3Q9MSwgaGp1c3Q9MSwpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmdyaWQuYXJyYW5nZShncmFmaWNvMSwgZ3JhZmljbzIsIGdyYWZpY28zLCBncmFmaWNvNCxucm93ID0gMikKYGBgCgojIyMgSnVnYWRvcmVzIHBvciBjb250aW5lbnRlCgpDb21vIGVyYSBkZSBlc3BlcmFyc2UsIHByZWRvbWluYW4gbG9zIGp1Z2Fkb3JlcyBldXJvcGVvcy4gTG9zIGFtZXJpY2Fub3MgeSBhZnJpY2Fub3MgZXN0w6FuIGNhc2kgZW4gaWd1YWwgbWVkaWRhLCBzaWVuZG8gdW4gcG9jbyBtYXlvciBsb3MgYW1lcmljYW5vcy4gQXNpYSB5IE9jZWFuw61hIGVuIGNvbmp1bnRvIGFwb3J0YW4gc29sbyB1bmEgbcOtbmltYSBjYW50aWRhZC4KCiMjIyBDYW50aWRhZCBkZSBqdWdhZG9yZXMgZGUgbGlnYQoKVG9kYXMgbGFzIGxpZ2FzIGVzdMOhbiBpZ3VhbCBkZSByZXByZXNlbnRhZGFzIGVuIGVsIGRhdGFzZXQsIGNvbiBsbyBjdWFsIGVsIGFuw6FsaXNpcyBkZWwgcHJlY2lvIGRlIGp1Z2Fkb3JlcyBzZWfDum4gbGEgbGlnYSBzZXLDoSBkZSBpbnRlcsOpcy4KCiMjIyBKdWdhZG9yZXMgcG9yIHBvc2ljacOzbgoKUHJlZG9taW5hbiBsb3MganVnYWRvcmVzIHF1ZSBzZSBkZXNlbXBlw7FhbiBlbiBlbCBjZW50cm8gZGVsIGNhbXBvLCBjb21vIGRlZmVuc29yZXMgY2VudHJhbGVzIG8gbWVkaW9jYW1waXN0YXMgY2VudHJhbGVzIG8gbWl4dG9zLiBFc3RvIHRpZW5lIGNpZXJ0YSBsw7NnaWNhIHlhIHF1ZSAzIG8gNCBqdWdhZG9yZXMgZGUgY2FtcG8gc2llbXByZSBzZSBkZXNlbXBlw7FhbiBlbiBlc3RhcyBwb3NpY2lvbmVzLiBMbGFtYSBsYSBhdGVuY2nDs24gbGEgYWx0YSBwcm9wb3JjacOzbiBkZSBjZW50cm9kZWxhbnRlcm9zLCB5YSBxdWUgbG9zIGVxdWlwb3Mgc3VlbGVuIHVzYXIgdW4gc29sbyBqdWdhZG9yIGVuIGVzYSBwb3NpY2nDs24uCgojIyMgSnVnYWRvcmVzIHBvciBwaWUgaMOhYmlsLgoKQ29tbyBlcmEgZGUgZXNwZXJhcnNlLCBtYXMgZGUgbGEgbWl0YWQgZGUgbG9zIGp1Z2Fkb3JlcyBzb24gZGVyZWNob3MuIFNpbiBlbWJhcmdvLCBjb21wYXJhZG8gY29uIGxhIHByb3BvcmNpw7NuIGRlIHBlcnNvbmFzIGRpZXN0cmFzIGVuIGVsIG11bmRvICg4NSUpLCBsYSBjYW50aWRhZCBkZSBqdWdhZG9yZXMgenVyZG9zIGVzIGJhc3RhbnRlIG1heW9yIGEgZXN0YXIgcHJvcG9yY2nDs24uCgojIyBDb3JyZWxvZ3JhbWEKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkZiAlPiUgCiAgZHBseXI6OnNlbGVjdChBZ2UsIEdscywgQXN0LCBwcmVjaW8pICU+JSAKICBtdXRhdGUobGlnYSA9IGRmJGN1cnJlbnRfY2x1Yl9kb21lc3RpY19jb21wZXRpdGlvbl9pZCkgJT4lCiAgZ2dwYWlycyguLCBtYXBwaW5nID0gYWVzKGNvbG91ciA9IGxpZ2EpLAogICAgICAgICAgdXBwZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJjb3IiLCBzaXplID0gMywgaGp1c3Q9MC41KSksIHByb2dyZXNzPUZBTFNFKSArIAogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChvcHRpb24gPSAiRCIpICsgCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gIkQiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh0aXRsZT0nQ29ycmVsb2dyYW1hIHZhcmlhYmxlcyBjb250aW51YXMnKQoKYGBgCgojIyMgQ29ycmVsYWNpw7NuIGVudHJlIEdvbGVzIHkgUHJlY2lvClNlIG9ic2VydmEgdW5hIGNvcnJlbGFjacOzbiBwb3NpdGl2YSBhbHRhIGEgbW9kZXJhZGEgKENvcnI6IDAuNTAyKS4gRXN0byBzdWdpZXJlIHF1ZSBsb3MganVnYWRvcmVzIHF1ZSBtYXJjYW4gbcOhcyBnb2xlcyB0aWVuZGVuIGEgdGVuZXIgdW4gdmFsb3IgZGUgbWVyY2FkbyBtw6FzIGFsdG8uIExhIHJlbGFjacOzbiBwYXJlY2Ugc2VyIGNvbnNpc3RlbnRlIGEgdHJhdsOpcyBkZSBsYXMgZGlmZXJlbnRlcyBsaWdhcy4KCiMjIyBDb3JyZWxhY2nDs24gZW50cmUgQXNpc3RlbmNpYXMgeSBQcmVjaW8gIApFeGlzdGUgdW5hIGNvcnJlbGFjacOzbiBwb3NpdGl2YSBtb2RlcmFkYSAoQ29ycjogMC40NjUpLiBJbmRpY2EgcXVlIGxvcyBqdWdhZG9yZXMgcXVlIGRhbiBtw6FzIGFzaXN0ZW5jaWFzIHRhbWJpw6luIHRpZW5kZW4gYSB0ZW5lciB1biBtYXlvciB2YWxvciBlbiBlbCBtZXJjYWRvLiBMYSBjb3JyZWxhY2nDs24gZXMgYWxnbyBtZW5vciBxdWUgY29uIGxvcyBnb2xlcywgc3VnaXJpZW5kbyBxdWUgZWwgbWVyY2FkbyB2YWxvcmEgbcOhcyBsYSBjYXBhY2lkYWQgZ29sZWFkb3JhLgoKIyMjIENvcnJlbGFjacOzbiBlbnRyZSBFZGFkIHkgUHJlY2lvClNlIG9ic2VydmEgdW5hIGNvcnJlbGFjacOzbiBuZWdhdGl2YSBiYWphIHBlcm8gY2xhcmEgKENvcnI6IC0wLjE3NikuIFN1Z2llcmUgcXVlIGVsIHZhbG9yIGRlIG1lcmNhZG8gdGllbmRlIGEgZGlzbWludWlyIGNvbiBsYSBlZGFkIGRlbCBqdWdhZG9yLiBFc3RvIHRpZW5lIHNlbnRpZG8gZGVzZGUgdW5hIHBlcnNwZWN0aXZhIGRlIGludmVyc2nDs24sIHlhIHF1ZSBsb3MganVnYWRvcmVzIG3DoXMgasOzdmVuZXMgdGllbmVuIG1heW9yIHBvdGVuY2lhbCBkZSBkZXNhcnJvbGxvIHkgYcOxb3MgZGUgY2FycmVyYSBwb3IgZGVsYW50ZS4KCiMjIyBEaXN0cmlidWNpw7NuIHBvciBMaWdhcwpMb3MgZGlhZ3JhbWFzIGRlIGNhamEgKGJveHBsb3RzKSBtdWVzdHJhbiBkaWZlcmVuY2lhcyBlbiBsYSBkaXN0cmlidWNpw7NuIGRlIHByZWNpb3MgZW50cmUgbGlnYXMuIExhcyBsaWdhcyBlc3Bhw7FvbGEgeSBicml0w6FuaWNhIHByZXNlbnRhbiB2YWxvcmVzIG3DoXMgYWx0b3MgZW4gZ2VuZXJhbC4gRXN0byBzZSBhbGluZWEgY29uIGVsIHBvZGVyIGVjb27Ds21pY28gZGUgZXN0YXMgbGlnYXMgeSBlc3BlY8OtZmljYW1lbnRlIGRlIGNsdWJlcyBjb21vIFJlYWwgTWFkcmlkIHkgTWFuY2hlc3RlciBDaXR5LiBMYSBkaXN0cmlidWNpw7NuIGRlIHByZWNpb3MgZXMgbm90YWJsZW1lbnRlIGFzaW3DqXRyaWNhLCBjb24gYWxndW5vcyB2YWxvcmVzIG11eSBhbHRvcyBxdWUgcG9kcsOtYW4gY29uc2lkZXJhcnNlIG91dGxpZXJzLgoKIyMjIENvcnJlbGFjaW9uZXMgY3J1emFkYXMKRXhpc3RlIHVuYSBjb3JyZWxhY2nDs24gcG9zaXRpdmEgbW9kZXJhZGEgZW50cmUgZ29sZXMgeSBhc2lzdGVuY2lhcyAoQ29ycjogMC41ODYpLiBFc3RvIHN1Z2llcmUgcXVlIGxvcyBqdWdhZG9yZXMgbcOhcyBlZmVjdGl2b3MgdGllbmRlbiBhIGRlc3RhY2FyIHRhbnRvIGVuIGdvbGVzIGNvbW8gZW4gYXNpc3RlbmNpYXMuIExhIGVkYWQgbXVlc3RyYSBjb3JyZWxhY2lvbmVzIG11eSBkw6liaWxlcyBjb24gZ29sZXMgeSBhc2lzdGVuY2lhcy4KCiMjIEp1Z2Fkb3JlcyBtYXMgY2Fyb3MKCkxvcyAxMCBqdWdhZG9yZXMgbWFzIGNhcm9zCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptYXNfY2Fyb3Nfbm9tYnJlIDwtIGRmICU+JSAKICBzbGljZV9tYXgob3JkZXJfYnkgPSBwcmVjaW8sIG49MTApICAlPiUgCiAgcHVsbChuYW1lKQptYXNfY2Fyb3NfZXF1aXBvIDwtIGRmICU+JSAKICBzbGljZV9tYXgob3JkZXJfYnkgPSBwcmVjaW8sIG49MTApICAlPiUgCiAgcHVsbChTcXVhZCkKbWFzX2Nhcm9zX3ByZWNpbyA8LSBkZiAlPiUgCiAgc2xpY2VfbWF4KG9yZGVyX2J5ID0gcHJlY2lvLCBuPTEwKSAgJT4lIAogIHB1bGwocHJlY2lvKQptYXNfY2FybyA8LSBkYXRhLmZyYW1lKE5vbWJyZSA9IG1hc19jYXJvc19ub21icmUsIAogICAgICAgICAgICAgICAgICAgICAgIEVxdWlwbyA9IG1hc19jYXJvc19lcXVpcG8sIAogICAgICAgICAgICAgICAgICAgICAgIFByZWNpbyA9IG1hc19jYXJvc19wcmVjaW8pCgprYWJsZShtYXNfY2FybykKYGBgCgojIyBKdWdhZG9yZXMgbWFzIGJhcmF0b3MKCkxvcyAxMCBqdWdhZG9yZXMgbWFzIGJhcmF0b3MKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1hc19iYXJhdG9zX25vbWJyZSA8LSBkZiAlPiUgCiAgc2xpY2VfbWluKG9yZGVyX2J5ID0gcHJlY2lvLCBuPTEwKSAgJT4lIAogIHB1bGwobmFtZSkKbWFzX2JhcmF0b3NfZXF1aXBvIDwtIGRmICU+JSAKICBzbGljZV9taW4ob3JkZXJfYnkgPSBwcmVjaW8sIG49MTApICAlPiUgCiAgcHVsbChTcXVhZCkKbWFzX2JhcmF0b3NfcHJlY2lvIDwtIGRmICU+JSAKICBzbGljZV9taW4ob3JkZXJfYnkgPSBwcmVjaW8sIG49MTApICAlPiUgCiAgcHVsbChwcmVjaW8pCm1hc19iYXJhdG8gPC0gZGF0YS5mcmFtZShOb21icmUgPSBtYXNfYmFyYXRvc19ub21icmUsIAogICAgICAgICAgICAgICAgICAgICAgIEVxdWlwbyA9IG1hc19iYXJhdG9zX2VxdWlwbywgCiAgICAgICAgICAgICAgICAgICAgICAgUHJlY2lvID0gbWFzX2JhcmF0b3NfcHJlY2lvKQoKa2FibGUobWFzX2JhcmF0bykKYGBgCgojIFJlZ3Jlc2nDs24gTGluZWFsIFNpbXBsZQoKIyMgTW9kZWxvICJHb2xlcyBzb24gYW1vcmVzIgoKUHJlY2lvID0gR29sZXMKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2RlbG9fY2xhc2ljb19nb2xlcyA9IGxtKGRhdGEgPSB0cmFpbl9kYXRhLCBmb3JtdWxhID0gcHJlY2lvIH4gR2xzKQpzdW1tYXJ5KG1vZGVsb19jbGFzaWNvX2dvbGVzKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGYsIGFlcyh4PSBHbHMsIHk9cHJlY2lvKSkrCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZV9idygpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHgsIGNvbG9yPSJmb3Jlc3RncmVlbiIsIHNlID0gRkFMU0UpCmBgYAoKCiMjIyBEaWFnbsOzc3RpY28KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRvc19hdWdtZW50YWRvcyA8LSBhdWdtZW50KG1vZGVsb19jbGFzaWNvX2dvbGVzKQpnMSA8LSBnZ3Bsb3QoZGF0b3NfYXVnbWVudGFkb3MsIGFlcyguZml0dGVkLCAucmVzaWQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKwogIGxhYnModGl0bGUgPSAiUmVzaWR1b3MgdnMgdmFsb3JlcyBwcmVkaWNob3MiKSArIAogIHRoZW1lX2J3KCkKZzIgPC0gZ2dwbG90KGRhdG9zX2F1Z21lbnRhZG9zLCBhZXMoc2FtcGxlID0gLnN0ZC5yZXNpZCkpICsKICBzdGF0X3FxKCkgKwogIGdlb21fYWJsaW5lKCkgKwogIGxhYnModGl0bGUgPSAiTm9ybWFsIFFRIHBsb3QiKSArIAogIHRoZW1lX2J3KCkKZzMgPC0gZ2dwbG90KGRhdG9zX2F1Z21lbnRhZG9zLCBhZXMoLmZpdHRlZCwgc3FydChhYnMoLnN0ZC5yZXNpZCkpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKyAKICB0aGVtZV9idygpICsKICBsYWJzKHRpdGxlID0gIlNjYWxlLWxvY2F0aW9uIHBsb3QiKQpnNCA8LSBnZ3Bsb3QoZGF0b3NfYXVnbWVudGFkb3MsIGFlcyguaGF0LCAuc3RkLnJlc2lkKSkgKwogIGdlb21fdmxpbmUoc2l6ZSA9IDIsIGNvbG91ciA9ICJ3aGl0ZSIsIHhpbnRlcmNlcHQgPSAwKSArCiAgZ2VvbV9obGluZShzaXplID0gMiwgY29sb3VyID0gIndoaXRlIiwgeWludGVyY2VwdCA9IDApICsKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArIAogIHRoZW1lX2J3KCkgKwogIGxhYnModGl0bGUgPSAiUmVzaWR1YWwgdnMgbGV2ZXJhZ2UiKQpncmlkLmFycmFuZ2UoZzEsIGcyLCBnMywgZzQsIG5yb3c9MikKYGBgCgojIyMjIERpYWduw7NzdGljbyBkZSBSZXNpZHVvcwotIEVsIGdyw6FmaWNvIGRlIHJlc2lkdW9zIHZzIHZhbG9yZXMgcHJlZGljaG9zIG11ZXN0cmEgdW4gcGF0csOzbiBkZSBlbWJ1ZG8sIGluZGljYW5kbyBoZXRlcm9jZWRhc3RpY2lkYWQKLSBFbCBRUS1wbG90IG11ZXN0cmEgZGVzdmlhY2lvbmVzIHNpZ25pZmljYXRpdmFzIGRlIGxhIG5vcm1hbGlkYWQsIGVzcGVjaWFsbWVudGUgZW4gbGFzIGNvbGFzCi0gRWwgU2NhbGUtbG9jYXRpb24gcGxvdCBjb25maXJtYSBsYSBoZXRlcm9jZWRhc3RpY2lkYWQsIGNvbiBtYXlvciB2YXJpYWJpbGlkYWQgZW4gbG9zIHZhbG9yZXMgcHJlZGljaG9zIG3DoXMgYWx0b3MKLSBFbCBncsOhZmljbyBkZSBsZXZlcmFnZSBtdWVzdHJhIHZhcmlvcyBwdW50b3MgY29uIGFsdGEgaW5mbHVlbmNpYSBxdWUgcG9kcsOtYW4gZXN0YXIgYWZlY3RhbmRvIGVsIG1vZGVsbwoKIyMjIyBJbnRlcnByZXRhY2nDs24gZGVsIE1vZGVsbwotIEVsIG1vZGVsbyBlc3RhYmxlY2UgdW5hIHJlbGFjacOzbiBsaW5lYWwgc2ltcGxlIGVudHJlIGVsIHByZWNpbyBkZWwganVnYWRvciB5IGxvcyBnb2xlcyBhbm90YWRvcwotIFBvciBjYWRhIGdvbCBhbm90YWRvLCBzZSBwcmVjaWJlIHVuIGF1bWVudG8gZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2byAocC12YWx1ZSA8IDAuMDUpIGVuIGVsIHByZWNpbyBkZWwganVnYWRvciBkZSDigqwzLDI2NywzNzUKLSBFbCBpbnRlcmNlcHRvIGVzIOKCrDUsMjM4LDQ1MiwgcXVlIHJlcHJlc2VudGEgZWwgcHJlY2lvIGJhc2UgZXN0aW1hZG8gcGFyYSB1biBqdWdhZG9yIHNpbiBnb2xlcwotIEVsIG1vZGVsbyBlcyBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZvIChwLXZhbHVlIDwgMi4yZS0xNikKLSBFbCBSwrIgYWp1c3RhZG8gZXMgMC4yOTg1LCBsbyBxdWUgaW5kaWNhIHF1ZSBlbCBtb2RlbG8gZXhwbGljYSBhcHJveGltYWRhbWVudGUgZWwgMzAlIGRlIGxhIHZhcmlhYmlsaWRhZCBlbiBsb3MgcHJlY2lvcwoKIyMjIyBFdmFsdWFjacOzbiBkZWwgUmVuZGltaWVudG8KCiMjIyMjIERhdG9zIGRlIGVudHJlbmFtaWVudG8KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnByZWRfbW9kZWxvX2NsYXNpY29fZ29sZXMgPC0gYXVnbWVudChtb2RlbG9fY2xhc2ljb19nb2xlcywgbmV3ZGF0YSA9IHRyYWluX2RhdGEpCmNhdCgiUk1TRTogIiwgcm1zZShkYXRhID0gcHJlZF9tb2RlbG9fY2xhc2ljb19nb2xlcywgdHJ1dGggPSBwcmVjaW8sIGVzdGltYXRlID0gLmZpdHRlZCkkLmVzdGltYXRlKQpgYGAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmNhdCgiTUFFOiAiLCBtYWUoZGF0YSA9IHByZWRfbW9kZWxvX2NsYXNpY29fZ29sZXMsIHRydXRoID0gcHJlY2lvLCBlc3RpbWF0ZSA9IC5maXR0ZWQpJC5lc3RpbWF0ZSkKYGBgCgojIyMjIyBEYXRvcyBkZSBwcnVlYmEKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnByZWRfbW9kZWxvX2NsYXNpY29fZ29sZXMgPC0gYXVnbWVudChtb2RlbG9fY2xhc2ljb19nb2xlcywgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKY2F0KCJSTVNFOiAiLCBybXNlKGRhdGEgPSBwcmVkX21vZGVsb19jbGFzaWNvX2dvbGVzLCB0cnV0aCA9IHByZWNpbywgZXN0aW1hdGUgPSAuZml0dGVkKSQuZXN0aW1hdGUpCmBgYApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2F0KCJNQUU6ICIsIG1hZShkYXRhID0gcHJlZF9tb2RlbG9fY2xhc2ljb19nb2xlcywgdHJ1dGggPSBwcmVjaW8sIGVzdGltYXRlID0gLmZpdHRlZCkkLmVzdGltYXRlKQpgYGAKLSBFbiBkYXRvcyBkZSBlbnRyZW5hbWllbnRvOgogIC0gUk1TRTogMTUsMzg4LDg2NgogIC0gTUFFOiA5LDE3OSw4MTMKLSBFbiBkYXRvcyBkZSBwcnVlYmE6CiAgLSBSTVNFOiAxNyw1MDMsODY5CiAgLSBNQUU6IDksNzMyLDU5OAotIExhIGRpZmVyZW5jaWEgcmVsYXRpdmFtZW50ZSBwZXF1ZcOxYSBlbnRyZSBsb3MgZXJyb3JlcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhIHN1Z2llcmUgcXVlIGVsIG1vZGVsbyBubyBlc3TDoSBzb2JyZWFqdXN0YWRvCgojIyMjIExpbWl0YWNpb25lcyBkZWwgTW9kZWxvCi0gTGEgcmVsYWNpw7NuIGxpbmVhbCBzaW1wbGUgcHVlZGUgc2VyIGRlbWFzaWFkbyBiw6FzaWNhIHBhcmEgY2FwdHVyYXIgbGEgY29tcGxlamlkYWQgZGVsIHByZWNpbyBkZSBsb3MganVnYWRvcmVzCi0gTGEgcHJlc2VuY2lhIGRlIGhldGVyb2NlZGFzdGljaWRhZCBzdWdpZXJlIHF1ZSBsYSB2YXJpYWJpbGlkYWQgZGVsIHByZWNpbyBhdW1lbnRhIGNvbiBlbCBuw7ptZXJvIGRlIGdvbGVzCi0gRWwgYmFqbyBSwrIgaW5kaWNhIHF1ZSBoYXkgb3Ryb3MgZmFjdG9yZXMgaW1wb3J0YW50ZXMgcXVlIG5vIGVzdMOhbiBzaWVuZG8gY29uc2lkZXJhZG9zCi0gTG9zIHN1cHVlc3RvcyBkZSBub3JtYWxpZGFkIHkgaG9tb2NlZGFzdGljaWRhZCBubyBzZSBjdW1wbGVuIGFkZWN1YWRhbWVudGUKCkVzdGUgbW9kZWxvLCBhdW5xdWUgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2bywgdGllbmUgbGltaXRhY2lvbmVzIGltcG9ydGFudGVzIHBhcmEgcHJlZGVjaXIgZWwgcHJlY2lvIGRlIGxvcyBqdWdhZG9yZXMuIExhIHZpb2xhY2nDs24gZGUgbG9zIHN1cHVlc3RvcyBiw6FzaWNvcyB5IGVsIGJham8gcG9kZXIgZXhwbGljYXRpdm8gc3VnaWVyZW4gcXVlIHNlIG5lY2VzaXRhIHVuIG1vZGVsbyBtw6FzIGNvbXBsZWpvIHF1ZSBpbmNvcnBvcmUgdmFyaWFibGVzIGFkaWNpb25hbGVzIHkgcG9zaWJsZW1lbnRlIHRyYW5zZm9ybWFjaW9uZXMgZGUgbGFzIHZhcmlhYmxlcyBleGlzdGVudGVzLgoKIyMgTW9kZWxvICJMYSBlZGFkIE5PIGVzIGxvIGRlIG1lbm9zIgoKUHJlY2lvID0gJEVkYWQkICsgJEVkYWReMiQKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2RlbG9fY2xhc2ljb19lZGFkID0gbW9kZWxvX2NsYXNpY29fZWRhZDIgPSBsbShkYXRhID0gdHJhaW5fZGF0YSwgZm9ybXVsYSA9IHByZWNpbyB+IEFnZSArIEkoQWdlXjIpKQpzdW1tYXJ5KG1vZGVsb19jbGFzaWNvX2VkYWQpCmBgYAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkZiwgYWVzKHg9IEFnZSwgeT1wcmVjaW8pKSsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2J3KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4geCArIEkoeF4yKSwgY29sb3I9ImZvcmVzdGdyZWVuIiwgc2UgPSBGQUxTRSkKYGBgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2F0KCJBIHBhcnRpciBkZSBsb3MiLCByb3VuZCg1MTEzNjE1LygyKjExMDE0NyksMCksICJhw7FvcywgZWwgcHJlY2lvIGRlIGxvcyBqdWdhZG9yZXMgY29taWVuemEgYSBkaXNtaW51aXIiKQpgYGAKCiMjIyBEaWFnbsOzc3RpY28KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRvc19hdWdtZW50YWRvcyA8LSBhdWdtZW50KG1vZGVsb19jbGFzaWNvX2VkYWQpCmcxIDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKC5maXR0ZWQsIC5yZXNpZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArCiAgbGFicyh0aXRsZSA9ICJSZXNpZHVvcyB2cyB2YWxvcmVzIHByZWRpY2hvcyIpICsgCiAgdGhlbWVfYncoKQpnMiA8LSBnZ3Bsb3QoZGF0b3NfYXVnbWVudGFkb3MsIGFlcyhzYW1wbGUgPSAuc3RkLnJlc2lkKSkgKwogIHN0YXRfcXEoKSArCiAgZ2VvbV9hYmxpbmUoKSArCiAgbGFicyh0aXRsZSA9ICJOb3JtYWwgUVEgcGxvdCIpICsgCiAgdGhlbWVfYncoKQpnMyA8LSBnZ3Bsb3QoZGF0b3NfYXVnbWVudGFkb3MsIGFlcyguZml0dGVkLCBzcXJ0KGFicyguc3RkLnJlc2lkKSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArIAogIHRoZW1lX2J3KCkgKwogIGxhYnModGl0bGUgPSAiU2NhbGUtbG9jYXRpb24gcGxvdCIpCmc0IDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKC5oYXQsIC5zdGQucmVzaWQpKSArCiAgZ2VvbV92bGluZShzaXplID0gMiwgY29sb3VyID0gIndoaXRlIiwgeGludGVyY2VwdCA9IDApICsKICBnZW9tX2hsaW5lKHNpemUgPSAyLCBjb2xvdXIgPSAid2hpdGUiLCB5aW50ZXJjZXB0ID0gMCkgKwogIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh0aXRsZSA9ICJSZXNpZHVhbCB2cyBsZXZlcmFnZSIpCmdyaWQuYXJyYW5nZShnMSwgZzIsIGczLCBnNCwgbnJvdz0yKQpgYGAKCiMjIyMgRGlhZ27Ds3N0aWNvIGRlIFJlc2lkdW9zCi0gRWwgZ3LDoWZpY28gZGUgcmVzaWR1b3MgdnMgdmFsb3JlcyBwcmVkaWNob3MgbXVlc3RyYSB1biBwYXRyw7NuIGRlIGVtYnVkbyBzaW1pbGFyIGFsIG1vZGVsbyBhbnRlcmlvcgotIEVsIFFRLXBsb3QgbXVlc3RyYSBkZXN2aWFjaW9uZXMgc2lnbmlmaWNhdGl2YXMgZGUgbGEgbm9ybWFsaWRhZCwgZXNwZWNpYWxtZW50ZSBlbiBsYXMgY29sYXMgc3VwZXJpb3JlcwotIEVsIFNjYWxlLWxvY2F0aW9uIHBsb3QgaW5kaWNhIGhldGVyb2NlZGFzdGljaWRhZAotIEVsIGdyw6FmaWNvIGRlIGxldmVyYWdlIG11ZXN0cmEgYWxndW5vcyBwdW50b3MgaW5mbHV5ZW50ZXMsIGF1bnF1ZSBtZW5vcyBwcm9udW5jaWFkb3MgcXVlIGVuIGVsIG1vZGVsbyBkZSBnb2xlcwoKIyMjIyBJbnRlcnByZXRhY2nDs24gZGVsIE1vZGVsbwotIEVsIG1vZGVsbyBlc3RhYmxlY2UgdW5hIHJlbGFjacOzbiBjdWFkcsOhdGljYSBlbnRyZSBlbCBwcmVjaW8gZGVsIGp1Z2Fkb3IgeSBzdSBlZGFkCi0gRWwgcHJlY2lvIGF1bWVudGEgY29uIGxhIGVkYWQgaGFzdGEgbG9zIDIzIGHDsW9zLCBwdW50byBhIHBhcnRpciBkZWwgY3VhbCBjb21pZW56YSBhIGRpc21pbnVpcgotIExvcyBjb2VmaWNpZW50ZXMgc29uOgogIC0gRWRhZDogKzUsMTEzLDYxNSAoZWZlY3RvIGxpbmVhbCBwb3NpdGl2bykKICAtIEVkYWTCsjogLTExMCwxNDcgKGVmZWN0byBjdWFkcsOhdGljbyBuZWdhdGl2bykKICAtIEludGVyY2VwdG86IC00NSw1NDcsMzA1Ci0gRWwgbW9kZWxvIGVzIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdm8gKHAtdmFsdWU6IDEuMjU1ZS0xMykKLSBFbCBSwrIgYWp1c3RhZG8gZXMgMC4wNDg1MywgbG8gcXVlIGluZGljYSBxdWUgZWwgbW9kZWxvIGV4cGxpY2Egc29sbyBhcHJveGltYWRhbWVudGUgZWwgNSUgZGUgbGEgdmFyaWFiaWxpZGFkIGVuIGxvcyBwcmVjaW9zCgojIyMjIEV2YWx1YWNpw7NuIGRlbCBSZW5kaW1pZW50bwoKIyMjIyMgRGF0b3MgZGUgZW50cmVuYW1pZW50bwpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcHJlZF9tb2RlbG9fY2xhc2ljb19lZGFkIDwtIGF1Z21lbnQobW9kZWxvX2NsYXNpY29fZWRhZCwgbmV3ZGF0YSA9IHRyYWluX2RhdGEpCmNhdCgiUk1TRTogIiwgcm1zZShkYXRhID0gcHJlZF9tb2RlbG9fY2xhc2ljb19lZGFkLCB0cnV0aCA9IHByZWNpbywgZXN0aW1hdGUgPSAuZml0dGVkKSQuZXN0aW1hdGUpCmBgYApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2F0KCJNQUU6ICIsIG1hZShkYXRhID0gcHJlZF9tb2RlbG9fY2xhc2ljb19lZGFkLCB0cnV0aCA9IHByZWNpbywgZXN0aW1hdGUgPSAuZml0dGVkKSQuZXN0aW1hdGUpCmBgYAoKIyMjIyMgRGF0b3MgZGUgcHJ1ZWJhCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcmVkX21vZGVsb19jbGFzaWNvX2VkYWQgPC0gYXVnbWVudChtb2RlbG9fY2xhc2ljb19lZGFkLCBuZXdkYXRhID0gdGVzdF9kYXRhKQpjYXQoIlJNU0U6ICIsIHJtc2UoZGF0YSA9IHByZWRfbW9kZWxvX2NsYXNpY29fZWRhZCwgdHJ1dGggPSBwcmVjaW8sIGVzdGltYXRlID0gLmZpdHRlZCkkLmVzdGltYXRlKQpgYGAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmNhdCgiTUFFOiAiLCBtYWUoZGF0YSA9IHByZWRfbW9kZWxvX2NsYXNpY29fZWRhZCwgdHJ1dGggPSBwcmVjaW8sIGVzdGltYXRlID0gLmZpdHRlZCkkLmVzdGltYXRlKQpgYGAKLSBFbiBkYXRvcyBkZSBlbnRyZW5hbWllbnRvOgogIC0gUk1TRTogMTcsOTE0LDYyNwogIC0gTUFFOiAxMCw1MTksMTI2Ci0gRW4gZGF0b3MgZGUgcHJ1ZWJhOgogIC0gUk1TRTogMTgsNDgyLDY4MwogIC0gTUFFOiAxMCw2NTUsODQ4Ci0gTG9zIGVycm9yZXMgc29uIG1heW9yZXMgcXVlIGVuIGVsIG1vZGVsbyBkZSBnb2xlcywgc3VnaXJpZW5kbyBxdWUgbGEgZWRhZCBwb3Igc8OtIHNvbGEgZXMgdW4gcHJlZGljdG9yIG3DoXMgZMOpYmlsIGRlbCBwcmVjaW8KCiMjIyMgTGltaXRhY2lvbmVzIGRlbCBNb2RlbG8KLSBFbCBtdXkgYmFqbyBSwrIgc3VnaWVyZSBxdWUgbGEgZWRhZCBwb3Igc8OtIHNvbGEgbm8gZXMgdW4gYnVlbiBwcmVkaWN0b3IgZGVsIHByZWNpbwotIExhIHJlbGFjacOzbiBjdWFkcsOhdGljYSBjYXB0dXJhIGVsIGhlY2hvIGRlIHF1ZSBsb3MganVnYWRvcmVzIGFsY2FuemFuIHVuIHBpY28gZGUgdmFsb3IsIHBlcm8gZWwgYWp1c3RlIGdlbmVyYWwgZXMgcG9icmUKLSBMb3Mgc3VwdWVzdG9zIGRlIG5vcm1hbGlkYWQgeSBob21vY2VkYXN0aWNpZGFkIHNpZ3VlbiBzaW4gY3VtcGxpcnNlCi0gRWwgbW9kZWxvIG5vIGNhcHR1cmEgb3Ryb3MgZmFjdG9yZXMgaW1wb3J0YW50ZXMgcXVlIGFmZWN0YW4gZWwgcHJlY2lvCgpFc3RlIG1vZGVsbyBjb25maXJtYSBxdWUgZXhpc3RlIHVuYSByZWxhY2nDs24gbm8gbGluZWFsIGVudHJlIGxhIGVkYWQgeSBlbCBwcmVjaW8gZGUgbG9zIGp1Z2Fkb3JlcywgY29uIHVuIHB1bnRvIG3DoXhpbW8gYWxyZWRlZG9yIGRlIGxvcyAyMyBhw7Fvcy4gU2luIGVtYmFyZ28sIHN1IGJham8gcG9kZXIgZXhwbGljYXRpdm8gc3VnaWVyZSBxdWUgbGEgZWRhZCBkZWJlIGNvbWJpbmFyc2UgY29uIG90cmFzIHZhcmlhYmxlcyBwYXJhIG9idGVuZXIgcHJlZGljY2lvbmVzIG3DoXMgcHJlY2lzYXMuCgoKIyBSZWdyZXNpw7NuIExpbmVhbCBNw7psdGlwbGUKCiMjIE1vZGVsbyAiQWhvcmEgdmEgZW4gc2VyaW8iCgokUHJlY2lvJCA9ICRHb2xlcyQgKyAkRWRhZCQgKyAkRWRhZF4yJCArICRBc2lzdGVuY2lhcyQgKyAkQ29udGluZW50ZSQgJGRlJCAkbmFjaW1pZW50byQgJGRlbCQgJGp1Z2Fkb3IkICsgJExpZ2EkICRkb25kZSQgJGp1ZWdhJAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1vZGVsb19jbGFzaWNvX211bHRpcGxlXzEgPSBsbShkYXRhID0gdHJhaW5fZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXVsYSA9IHByZWNpbyB+IEdscyArIEFnZSArIEkoQWdlXjIpICsgQXN0ICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250aW5lbnRlICsgY3VycmVudF9jbHViX2RvbWVzdGljX2NvbXBldGl0aW9uX2lkKQpzdW1tYXJ5KG1vZGVsb19jbGFzaWNvX211bHRpcGxlXzEpCmBgYAoKIyMjIERpYWduw7NzdGljbwoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRhdG9zX2F1Z21lbnRhZG9zIDwtIGF1Z21lbnQobW9kZWxvX2NsYXNpY29fbXVsdGlwbGVfMSkKZzUgPC0gZ2dwbG90KGRhdG9zX2F1Z21lbnRhZG9zLCBhZXMoLmZpdHRlZCwgLnJlc2lkKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsKICBsYWJzKHRpdGxlID0gIlJlc2lkdW9zIHZzIHZhbG9yZXMgcHJlZGljaG9zIikgKyAKICB0aGVtZV9idygpCmc2IDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKHNhbXBsZSA9IC5zdGQucmVzaWQpKSArCiAgc3RhdF9xcSgpICsKICBnZW9tX2FibGluZSgpICsKICBsYWJzKHRpdGxlID0gIk5vcm1hbCBRUSBwbG90IikgKyAKICB0aGVtZV9idygpCmc3IDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKC5maXR0ZWQsIHNxcnQoYWJzKC5zdGQucmVzaWQpKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh0aXRsZSA9ICJTY2FsZS1sb2NhdGlvbiBwbG90IikKZzggPC0gZ2dwbG90KGRhdG9zX2F1Z21lbnRhZG9zLCBhZXMoLmhhdCwgLnN0ZC5yZXNpZCkpICsKICBnZW9tX3ZsaW5lKHNpemUgPSAyLCBjb2xvdXIgPSAid2hpdGUiLCB4aW50ZXJjZXB0ID0gMCkgKwogIGdlb21faGxpbmUoc2l6ZSA9IDIsIGNvbG91ciA9ICJ3aGl0ZSIsIHlpbnRlcmNlcHQgPSAwKSArCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKyAKICB0aGVtZV9idygpICsKICBsYWJzKHRpdGxlID0gIlJlc2lkdWFsIHZzIGxldmVyYWdlIikKZ3JpZC5hcnJhbmdlKGc1LCBnNiwgZzcsIGc4LCBucm93PTIpCmBgYAoKIyMjIyBEaWFnbsOzc3RpY28gZGUgUmVzaWR1b3MKLSBFbCBncsOhZmljbyBkZSByZXNpZHVvcyB2cyB2YWxvcmVzIHByZWRpY2hvcyBtdWVzdHJhIHVuYSBtZWpvcmEgZW4gZWwgcGF0csOzbiBkZSBoZXRlcm9jZWRhc3RpY2lkYWQKLSBFbCBRUS1wbG90IGluZGljYSB1bmEgbWVqb3IgYXByb3hpbWFjacOzbiBhIGxhIG5vcm1hbGlkYWQgZW4gZWwgY2VudHJvIGRlIGxhIGRpc3RyaWJ1Y2nDs24KLSBFbCBTY2FsZS1sb2NhdGlvbiBwbG90IG11ZXN0cmEgdW5hIHZhcmlhYmlsaWRhZCBtw6FzIGVzdGFibGUgcXVlIGxvcyBtb2RlbG9zIGFudGVyaW9yZXMKLSBFbCBncsOhZmljbyBkZSBsZXZlcmFnZSBpZGVudGlmaWNhIG1lbm9zIHB1bnRvcyBpbmZsdXllbnRlcyBleHRyZW1vcwoKIyMjIyBJbnRlcnByZXRhY2nDs24gZGVsIE1vZGVsbwotIEVsIG1vZGVsbyBpbmNvcnBvcmEgbcO6bHRpcGxlcyB2YXJpYWJsZXMgcHJlZGljdG9yYXM6IGdvbGVzLCBlZGFkIChsaW5lYWwgeSBjdWFkcsOhdGljYSksIGFzaXN0ZW5jaWFzLCBjb250aW5lbnRlIGRlIG9yaWdlbiB5IGxpZ2EKLSBMb3MgY29lZmljaWVudGVzIG3DoXMgc2lnbmlmaWNhdGl2b3Mgc29uOgogIC0gR29sZXM6ICsyLDE4OSw4NTUgcG9yIGdvbCAocC12YWx1ZSA8IDJlLTE2KS4gRXN0byBpbmRpY2EgcXVlIGVsIHByZWNpbyBkZSBsb3MganVnYWRvcmVzIHF1ZSB0aWVuZW4gbWlzbWEgY2FudGlkYWQgZGUgYXNpc3RlbmNpYXMgeSBlZGFkIGF1bWVudGEg4oKsMiwxODksODU1IHBvciBjYWRhIGdvbCBhbm90YWRvCiAgLSBBc2lzdGVuY2lhczogKzIsMTUyLDYwNyBwb3IgYXNpc3RlbmNpYSAocC12YWx1ZSA8IDJlLTE2KS4gRXN0byBpbmRpY2EgcXVlIGVsIHByZWNpbyBkZSBsb3MganVnYWRvcmVzIHF1ZSB0aWVuZW4gbWlzbWEgY2FudGlkYWQgZGUgZ29sZXMgeSBlZGFkIGF1bWVudGEg4oKsMiwxNTIsNjA3IHBvciBjYWRhIGFzaXN0ZW5jaWEgYWRpY2lvbmFsLgogIC0gRWRhZDogZWZlY3RvIGN1YWRyw6F0aWNvIGNvbiBtw6F4aW1vIGFscmVkZWRvciBkZSBsb3MgMjMgYcOxb3MKICAtIEFtw6lyaWNhOiArNCw0OTQsMTEzIHJlc3BlY3RvIGEgw4FmcmljYSAocC12YWx1ZSA9IDAuMDE1KQogIC0gUHJlbWllciBMZWFndWU6ICsxMSwwNjAsMTE2IHJlc3BlY3RvIGEgbGEgbGlnYSBkZSByZWZlcmVuY2lhLCBMYSBMaWdhIChFc3Bhw7FhKSAocC12YWx1ZSA8IDJlLTE2KQogIC0gTGlndWUgMTogLTQsNDM2LDkxNyByZXNwZWN0byBhIGxhIGxpZ2EgZGUgcmVmZXJlbmNpYSwgTGEgTGlnYSAocC12YWx1ZSA8IDAuMDUpCiAgLSBCdW5kZXNsaWdhOiAtNCwwMTEsODA2IHJlc3BlY3RvIGEgbGEgbGlnYSBkZSByZWZlcmVuY2lhLCBMYSBMaWdhIChwLXZhbHVlIDwgMC4wNSkKICAtIFNlcmllIEEgKEl0YWxpYSk6IC0yLDQyOSwwNjAgcmVzcGVjdG8gYSBsYSBsaWdhIGRlIHJlZmVyZW5jaWEsIExhIExpZ2EgKHAtdmFsdWUgPCAwLjA1KQogIAotIEVsIFLCsiBhanVzdGFkbyBlcyAwLjQ3MzMsIGluZGljYW5kbyBxdWUgZWwgbW9kZWxvIGV4cGxpY2EgYXByb3hpbWFkYW1lbnRlIGVsIDQ3JSBkZSBsYSB2YXJpYWJpbGlkYWQKCiMjIyMgRXZhbHVhY2nDs24gZGVsIFJlbmRpbWllbnRvCgojIyMjIyBEYXRvcyBkZSBlbnRyZW5hbWllbnRvCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcmVkX21vZGVsb19jbGFzaWNvX211bHRpcGxlXzEgPC0gYXVnbWVudChtb2RlbG9fY2xhc2ljb19tdWx0aXBsZV8xLCBuZXdkYXRhID0gdHJhaW5fZGF0YSkKY2F0KCJSTVNFOiAiLCBybXNlKGRhdGEgPSBwcmVkX21vZGVsb19jbGFzaWNvX211bHRpcGxlXzEsIHRydXRoID0gcHJlY2lvLCBlc3RpbWF0ZSA9IC5maXR0ZWQpJC5lc3RpbWF0ZSkKYGBgCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjYXQoIk1BRTogIiwgbWFlKGRhdGEgPSBwcmVkX21vZGVsb19jbGFzaWNvX211bHRpcGxlXzEsIHRydXRoID0gcHJlY2lvLCBlc3RpbWF0ZSA9IC5maXR0ZWQpJC5lc3RpbWF0ZSkKYGBgCgojIyMjIyBEYXRvcyBkZSBwcnVlYmEKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnByZWRfbW9kZWxvX2NsYXNpY29fbXVsdGlwbGVfMSA8LSBhdWdtZW50KG1vZGVsb19jbGFzaWNvX211bHRpcGxlXzEsIG5ld2RhdGEgPSB0ZXN0X2RhdGEpCmNhdCgiUk1TRTogIiwgcm1zZShkYXRhID0gcHJlZF9tb2RlbG9fY2xhc2ljb19tdWx0aXBsZV8xLCB0cnV0aCA9IHByZWNpbywgZXN0aW1hdGUgPSAuZml0dGVkKSQuZXN0aW1hdGUpCmBgYApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2F0KCJNQUU6ICIsIG1hZShkYXRhID0gcHJlZF9tb2RlbG9fY2xhc2ljb19tdWx0aXBsZV8xLCB0cnV0aCA9IHByZWNpbywgZXN0aW1hdGUgPSAuZml0dGVkKSQuZXN0aW1hdGUpCmBgYAotIEVuIGRhdG9zIGRlIGVudHJlbmFtaWVudG86CiAgLSBSTVNFOiAxMywyNzYsOTEzCiAgLSBNQUU6IDgsMDkzLDI4MAotIEVuIGRhdG9zIGRlIHBydWViYToKICAtIFJNU0U6IDE1LDQ1MCwxODAKICAtIE1BRTogOCw0NjksOTA2Ci0gTGEgZGlmZXJlbmNpYSBtb2RlcmFkYSBlbnRyZSBlcnJvcmVzIGRlIGVudHJlbmFtaWVudG8geSBwcnVlYmEgc3VnaWVyZSB1biBuaXZlbCBhY2VwdGFibGUgZGUgZ2VuZXJhbGl6YWNpw7NuCgojIyMjIE1lam9yYXMgUmVzcGVjdG8gYSBNb2RlbG9zIEFudGVyaW9yZXMKLSBFbCBSwrIgYWp1c3RhZG8gYXVtZW50w7Mgc2lnbmlmaWNhdGl2YW1lbnRlIChkZSAwLjMwIHkgMC4wNSBhIDAuNDcpCi0gTG9zIGVycm9yZXMgZGUgcHJlZGljY2nDs24gKFJNU0UgeSBNQUUpIGRpc21pbnV5ZXJvbgotIExvcyBkaWFnbsOzc3RpY29zIGRlIHJlc2lkdW9zIG11ZXN0cmFuIG1lam9yZXMgcHJvcGllZGFkZXMgZXN0YWTDrXN0aWNhcwotIExhIGluY29ycG9yYWNpw7NuIGRlIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMgY2FwdHVyYSBlZmVjdG9zIGVzcGVjw61maWNvcyBwb3IgY29udGluZW50ZSB5IGxpZ2EKCkVzdGUgbW9kZWxvIHJlcHJlc2VudGEgdW5hIG1lam9yYSBzdXN0YW5jaWFsIHNvYnJlIGxvcyBtb2RlbG9zIHNpbXBsZXMgYW50ZXJpb3JlcywgY2FwdHVyYW5kbyBlZmVjdG9zIG3DoXMgY29tcGxlam9zIHkgcmVkdWNpZW5kbyBsb3MgZXJyb3JlcyBkZSBwcmVkaWNjacOzbi4gU2luIGVtYmFyZ28sIGHDum4gaGF5IGVzcGFjaW8gcGFyYSBtZWpvcmFzLCBlc3BlY2lhbG1lbnRlIGVuIGVsIHRyYXRhbWllbnRvIGRlIHZhbG9yZXMgZXh0cmVtb3MgeSBsYSBwb3NpYmxlIGluY29ycG9yYWNpw7NuIGRlIG3DoXMgdmFyaWFibGVzIHJlbGV2YW50ZXMuCgoKIyMgTW9kZWxvICJBaG9yYSB2YSBlbiBzZXJpbyAyIgoKJGxvZyhQcmVjaW8pJCA9ICRHb2xlcyQgKyAkRWRhZCQgKyAkRWRhZF4yJCArICRBc2lzdGVuY2lhcyQgKyAkQ29udGluZW50ZSQgJGRlJCAkbmFjaW1pZW50byQgJGRlbCQgJGp1Z2Fkb3IkICsgJExpZ2EkICRkb25kZSQgJGp1ZWdhJAoKQWwgdXRpbGl6YXIgdW5hIHRyYW5zZm9ybWFjacOzbiBsb2dhcsOtdG1pY2EsIGxhIGludGVycHJldGFjacOzbiBkZWwgbW9kZWxvIGVzIGRpZmVyZW50ZS4gTGEgdmFyaWFjacOzbiBkZSBsYSB2YXJpYWJsZSBhIHByZWRlY2lyIGVzIGVuIHTDqXJtaW5vcyBwb3JjZW50dWFsZXMgc2Vnw7puIGF1bWVudGEgZW4gdW5hIHVuaWRhZCBsYSB2YXJpYWJsZSBwcmVkaWN0b3JhIG51bcOpcmljYSBvIHNlZ8O6biBsYSB2YXJpYWJsZSBkZSByZWZlcmVuY2lhIGNhdGVnw7NyaWNhLgoKUG9yIGxvIHRhbnRvOgoKJCQgCmMgPSAoXGV4cChiKSAtIDEpIFx0aW1lcyAxMDAgXHF1YWQgXHRleHR7KDEzKX0KJCQgCgpEb25kZQoKJCQgCnkgPSBhICsgYiBcdGltZXMgeCBccXVhZCBcdGV4dHsoMTQpfQokJCAKCkVuIGVzdGUgY2Fzbywgc2kgJHgkIGVzIHVuYSB2YXJpYWJsZSBudW3DqXJpY2EgZW50b25jZXMgJHkkIHZhcsOtYSAkYyQlIHBvciBjYWRhIGF1bWVudG8gZGUgJHgkIGVuIHVuYSB1bmlkYWQuIFNpIGVuIGNhbWJpbyAkeCQgZXMgdW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhLCAkeSQgdmFyw61hICRjJCUgY29uIHJlc3BlY3RvIGEgbGEgdmFyaWFibGUgY2F0ZWfDs3JpY2EgZGUgcmVmZXJlbmNpYS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2RlbG9fY2xhc2ljb19tdWx0aXBsZSA9IGxtKGRhdGEgPSB0cmFpbl9kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtdWxhID0gbG9nKHByZWNpbykgfiBHbHMgKyBBZ2UgKyBJKEFnZV4yKSArIEFzdCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGluZW50ZSArIGN1cnJlbnRfY2x1Yl9kb21lc3RpY19jb21wZXRpdGlvbl9pZCkKc3VtbWFyeShtb2RlbG9fY2xhc2ljb19tdWx0aXBsZSkKYGBgCgoKIyMjIERpYWduw7NzdGljbwoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRhdG9zX2F1Z21lbnRhZG9zIDwtIGF1Z21lbnQobW9kZWxvX2NsYXNpY29fbXVsdGlwbGUpCmc5IDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKC5maXR0ZWQsIC5yZXNpZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArCiAgbGFicyh0aXRsZSA9ICJSZXNpZHVvcyB2cyB2YWxvcmVzIHByZWRpY2hvcyIpICsgCiAgdGhlbWVfYncoKQpnMTAgPC0gZ2dwbG90KGRhdG9zX2F1Z21lbnRhZG9zLCBhZXMoc2FtcGxlID0gLnN0ZC5yZXNpZCkpICsKICBzdGF0X3FxKCkgKwogIGdlb21fYWJsaW5lKCkgKwogIGxhYnModGl0bGUgPSAiTm9ybWFsIFFRIHBsb3QiKSArIAogIHRoZW1lX2J3KCkKZzExIDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKC5maXR0ZWQsIHNxcnQoYWJzKC5zdGQucmVzaWQpKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh0aXRsZSA9ICJTY2FsZS1sb2NhdGlvbiBwbG90IikKZzEyIDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKC5oYXQsIC5zdGQucmVzaWQpKSArCiAgZ2VvbV92bGluZShzaXplID0gMiwgY29sb3VyID0gIndoaXRlIiwgeGludGVyY2VwdCA9IDApICsKICBnZW9tX2hsaW5lKHNpemUgPSAyLCBjb2xvdXIgPSAid2hpdGUiLCB5aW50ZXJjZXB0ID0gMCkgKwogIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh0aXRsZSA9ICJSZXNpZHVhbCB2cyBsZXZlcmFnZSIpCmdyaWQuYXJyYW5nZShnOSwgZzEwLCBnMTEsIGcxMiwgbnJvdz0yKQpgYGAKCiMjIyMgRGlhZ27Ds3N0aWNvIGRlIFJlc2lkdW9zCi0gRWwgZ3LDoWZpY28gZGUgcmVzaWR1b3MgdnMgdmFsb3JlcyBwcmVkaWNob3MgbXVlc3RyYSB1bmEgZGlzdHJpYnVjacOzbiBtw6FzIGhvbW9nw6luZWEKLSBFbCBRUS1wbG90IGluZGljYSB1bmEgbm90YWJsZSBtZWpvcmEgZW4gbGEgbm9ybWFsaWRhZCBkZSBsb3MgcmVzaWR1b3MKLSBFbCBTY2FsZS1sb2NhdGlvbiBwbG90IG11ZXN0cmEgdW5hIHZhcmlhbnphIG3DoXMgZXN0YWJsZQotIEVsIGdyw6FmaWNvIGRlIGxldmVyYWdlIHN1Z2llcmUgbWVub3MgaW5mbHVlbmNpYSBkZSB2YWxvcmVzIGV4dHJlbW9zCgojIyMjIEludGVycHJldGFjacOzbiBkZWwgTW9kZWxvCi0gRWwgbW9kZWxvIHV0aWxpemEgbGEgdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYSBkZSBsYSB2YXJpYWJsZSBwcmVjaW8geSBtYW50aWVuZSBsYXMgbWlzbWFzIHZhcmlhYmxlcyBwcmVkaWN0b3JhcwotIExvcyBjb2VmaWNpZW50ZXMgbcOhcyBzaWduaWZpY2F0aXZvcyBzb246CiAgLSBHb2xlczogKzAuMTE2IChwLXZhbHVlIDwgMmUtMTYpLkVzdG8gaW5kaWNhIHF1ZSBlbCBwcmVjaW8gZGUgbG9zIGp1Z2Fkb3JlcyBxdWUgdGllbmVuIG1pc21hIGNhbnRpZGFkIGRlIGFzaXN0ZW5jaWFzIHkgZWRhZCBhdW1lbnRhIHVuIDEyLjMlIHBvciBjYWRhIGdvbCBhbm90YWRvLgogIC0gQXNpc3RlbmNpYXM6ICswLjE3MyAocC12YWx1ZSA8IDJlLTE2KS4gRXN0byBpbmRpY2EgcXVlIGVsIHByZWNpbyBkZSBsb3MganVnYWRvcmVzIHF1ZSB0aWVuZW4gbWlzbWEgY2FudGlkYWQgZGUgZ29sZXMgeSBlZGFkIGF1bWVudGEgdW4gMTguOSUgcG9yIGNhZGEgYXNpc3RlbmNpYSBhZGljaW9uYWwuCiAgLSBFZGFkOiBlZmVjdG8gY3VhZHLDoXRpY28gc2lnbmlmaWNhdGl2byAocC12YWx1ZSA8IDJlLTE2KQogIC0gQW3DqXJpY2E6ICswLjQwNCByZXNwZWN0byBhIMOBZnJpY2EgKHAtdmFsdWUgPSAwLjAwNyksIGluZGljYSB1biBhdW1lbnRvIGVzdGFkw61zdGljYW1ldGUgc2lnbmlmaWNhdGl2byBkZSA0OS44JSBlbiBlbCBwcmVjaW8gZGUgbG9zIGp1Z2Fkb3JlcyBhbWVyaWNhbm9zIGNvbiByZXNwZWN0byBhIGxvcyBhZnJpY2Fub3MuCiAgLSBQcmVtaWVyIExlYWd1ZTogKzAuODE4IHJlc3BlY3RvIGEgbGEgbGlnYSBkZSByZWZlcmVuY2lhLCAocC12YWx1ZSA8IDJlLTE2KSBpbmRpY2EgdW4gYXVtZW50byBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZvIGRlIDEyNi41JSBlbiBlbCBwcmVjaW8gZGUgbG9zIGp1Z2Fkb3JlcyBkZSBsYSBQcmVtaWVyIExlYWd1ZSBjb24gcmVzcGVjdG8gYSBsb3MganVnYWRvcmVzIGRlIExhIExpZ2EgKEVzcGHDsWEpCiAgLSBMaWd1ZSAxIChGcmFuY2lhKTogLTAuMjk2IHJlc3BlY3RvIGEgbGEgbGlnYSBkZSByZWZlcmVuY2lhLCAocC12YWx1ZSA9IDAuMDAzKSBpbmRpY2EgdW5hIGRpc21pbnVjacOzbiBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZhIGRlIDI1LjYlIGVuIGVsIHByZWNpbyBkZSBsb3MganVnYWRvcmVzIGRlIGxhIExpZ3VlIDEgY29uIHJlc3BlY3RvIGEgbG9zIGp1Z2Fkb3JlcyBkZSBMYSBMaWdhLgogIC0gQnVuZGVzbGlnYTogLTAuMjg1IHJlc3BlY3RvIGRlIGxhIGxpZ2EgZGUgcmVmZXJlbmNpYSAocC12YWx1ZSA9IDAuMDA1KSBpbmRpY2EgdW5hIGRpc21pbnVjacOzbiBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZhIGRlIDI0LjglIGVuIGVsIHByZWljaW8gZGUgbG9zIGp1Z2Fkb3JlcyBkZSBsYSBCdW5kZXNsaWdhIGNvbiByZXNwZWN0byBhIExhIExpZ2EuCi0gRWwgUsKyIGFqdXN0YWRvIGVzIDAuNDYxOSwgc2ltaWxhciBhbCBtb2RlbG8gYW50ZXJpb3IgcGVybyBjb24gbWVqb3IgaW50ZXJwcmV0YWJpbGlkYWQKCiMjIyMgRXZhbHVhY2nDs24gZGVsIFJlbmRpbWllbnRvCgojIyMjIyBEYXRvcyBkZSBlbnRyZW5hbWllbnRvCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcmVkX21vZGVsb19jbGFzaWNvX211bHRpcGxlIDwtIGF1Z21lbnQobW9kZWxvX2NsYXNpY29fbXVsdGlwbGUsIG5ld2RhdGEgPSB0cmFpbl9kYXRhKQpwcmVkX21vZGVsb19jbGFzaWNvX211bHRpcGxlJGV4cF9maXR0ZWQgPC0gZXhwKHByZWRfbW9kZWxvX2NsYXNpY29fbXVsdGlwbGUkLmZpdHRlZCkKY2F0KCJSTVNFOiAiLCBybXNlKGRhdGEgPSBwcmVkX21vZGVsb19jbGFzaWNvX211bHRpcGxlLCB0cnV0aCA9IHByZWNpbywgZXN0aW1hdGUgPSBleHBfZml0dGVkKSQuZXN0aW1hdGUpCmBgYApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2F0KCJNQUU6ICIsIG1hZShkYXRhID0gcHJlZF9tb2RlbG9fY2xhc2ljb19tdWx0aXBsZSwgdHJ1dGggPSBwcmVjaW8sIGVzdGltYXRlID0gZXhwX2ZpdHRlZCkkLmVzdGltYXRlKQpgYGAKCiMjIyMjIERhdG9zIGRlIHBydWViYQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcHJlZF9tb2RlbG9fY2xhc2ljb19tdWx0aXBsZSA8LSBhdWdtZW50KG1vZGVsb19jbGFzaWNvX211bHRpcGxlLCBuZXdkYXRhID0gdGVzdF9kYXRhKQpwcmVkX21vZGVsb19jbGFzaWNvX211bHRpcGxlJGV4cF9maXR0ZWQgPC0gZXhwKHByZWRfbW9kZWxvX2NsYXNpY29fbXVsdGlwbGUkLmZpdHRlZCkKY2F0KCJSTVNFOiAiLCBybXNlKGRhdGEgPSBwcmVkX21vZGVsb19jbGFzaWNvX211bHRpcGxlLCB0cnV0aCA9IHByZWNpbywgZXN0aW1hdGUgPSBleHBfZml0dGVkKSQuZXN0aW1hdGUpCmBgYApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2F0KCJNQUU6ICIsIG1hZShkYXRhID0gcHJlZF9tb2RlbG9fY2xhc2ljb19tdWx0aXBsZSwgdHJ1dGggPSBwcmVjaW8sIGVzdGltYXRlID0gZXhwX2ZpdHRlZCkkLmVzdGltYXRlKQpgYGAKLSBFbiBkYXRvcyBkZSBlbnRyZW5hbWllbnRvOgogIC0gUk1TRTogMjgsNTAwLDU5OAogIC0gTUFFOiA4LDA1Nyw3ODEKLSBFbiBkYXRvcyBkZSBwcnVlYmE6CiAgLSBSTVNFOiAxNyw1MDgsNDU2CiAgLSBNQUU6IDcsMzU3LDYxNgotIExhIHRyYW5zZm9ybWFjacOzbiBsb2dhcsOtdG1pY2EgbWVqb3JhIGVsIE1BRSBlbiBsb3MgZGF0b3MgZGUgcHJ1ZWJhLCBhdW5xdWUgZWwgUk1TRSBlcyBtw6FzIGFsdG8KCiMjIyBNZWpvcmFzIFJlc3BlY3RvIGFsIE1vZGVsbyBBbnRlcmlvcgotIE1lam9yIGludGVycHJldGFiaWxpZGFkIGRlIGxvcyBjb2VmaWNpZW50ZXMgZW4gdMOpcm1pbm9zIGRlIHBvcmNlbnRhamVzCi0gTWVqb3IgY3VtcGxpbWllbnRvIGRlIGxvcyBzdXB1ZXN0b3MgZGUgbm9ybWFsaWRhZCB5IGhvbW9jZWRhc3RpY2lkYWQKLSBSZWR1Y2Npw7NuIGRlbCBNQUUgZW4gZGF0b3MgZGUgcHJ1ZWJhCi0gTWF5b3IgZXN0YWJpbGlkYWQgZW4gbGEgcHJlZGljY2nDs24gZGUgdmFsb3JlcyBleHRyZW1vcwoKTGEgdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYSBkZWwgcHJlY2lvIG1lam9yYSBsYXMgcHJvcGllZGFkZXMgZXN0YWTDrXN0aWNhcyBkZWwgbW9kZWxvIHkgZmFjaWxpdGEgbGEgaW50ZXJwcmV0YWNpw7NuIGRlIGxvcyBlZmVjdG9zLiBFc3RlIG1vZGVsbyBwYXJlY2UgbcOhcyBhZGVjdWFkbyBwYXJhIHByZWRlY2lyIGVsIHByZWNpbyBkZSBsb3MganVnYWRvcmVzLCBlc3BlY2lhbG1lbnRlIGN1YW5kbyBzZSBjb25zaWRlcmEgbGEgaW50ZXJwcmV0YWJpbGlkYWQgeSBsYSBlc3RhYmlsaWRhZCBkZSBsYXMgcHJlZGljY2lvbmVzLgoKCiMgUmVncmVzacOzbiBMaW5lYWwgTcO6bHRpcGxlIFJvYnVzdGEKCiMjIE1vZGVsbyAiR29sZXMgc29uIGFtb3JlcyBST0JVU1RPUyIKClByZWNpbyA9IEdvbGVzCgpTZSB1dGlsaXphIGVsIG1vZGVsbyBiYXNlIGRlIFJvYnVzdGJhc2UsIGRvbmRlIGxhIGZ1Y2nDs24gZGUgcMOpcmRpZGEgdXRpbGl6YWRhIGVzIGxhIGJpY3VhZHJhZGEgeSB1dGlsaXphIGVsIGVzdGltYWRvciBNTS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2RlbG9fbG1yb2JfZ29sZXMgPC0gbG1yb2IoZm9ybXVsYSA9IHByZWNpbyB+IEdscywgZGF0YT10cmFpbl9kYXRhKQpwcmVkX21vZGVsb19sbXJvYl9nb2xlcyA8LSBkYXRhLmZyYW1lKAogIEdscyA9IHRyYWluX2RhdGEkR2xzLAogIHByZWNpb19wcmVkID0gcHJlZGljdChtb2RlbG9fbG1yb2JfZ29sZXMsIG5ld2RhdGEgPSB0cmFpbl9kYXRhKQopCnN1bW1hcnkobW9kZWxvX2xtcm9iX2dvbGVzKQpgYGAKCkVsIHNpZ3VpZW50ZSBncsOhZmljbyBtdWVzdHJhIGxhIGxpbmVhIGRlIHJlZ3Jlc2nDs24gZGVsIG1vZGVsbyBsaW5lYWwgY2zDoXNpY28gKHZpb2xldGEpIGZyZW50ZSBhIGxhIHJlZ3Jlc2nDs24gZGVsIG1vZGVsbyByb2J1c3RvIChhbWFyaWxsbykuCgpTZSBwdWVkZSBhcHJlY2lhciBtdXkgY2xhcmFtZW50ZSBjb21vIGVsIG1vZGVsbyByb2J1c3RvIHRpZW5lIHVuYSBwZW5kaWVudGUgbWFzIGJhamEgYWp1c3RhbmRvIGVuIG1lbm9yIG1lZGlkYSBhIGxvcyBqdWdhZG9yZXMgZGUgbWF5b3IgY290aXphY2nDs24uCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KHRyYWluX2RhdGEsIGFlcyh4ID0gR2xzLCB5ID0gcHJlY2lvKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiB4LCBjb2xvcj0iZGFya3Zpb2xldCIsIHNlID0gRkFMU0UpICsKICBnZW9tX2xpbmUoZGF0YSA9IHByZWRfbW9kZWxvX2xtcm9iX2dvbGVzLCBhZXMoeCA9IEdscywgeSA9IHByZWNpb19wcmVkKSwgY29sb3IgPSAieWVsbG93IikgKwogIHRoZW1lX2J3KCkKYGBgCgojIyMjIEludGVycHJldGFjacOzbiBkZWwgTW9kZWxvCi0gRWwgbW9kZWxvIGVzdGFibGVjZSB1bmEgcmVsYWNpw7NuIGxpbmVhbCBzaW1wbGUgZW50cmUgZWwgcHJlY2lvIGRlbCBqdWdhZG9yIHkgbG9zIGdvbGVzIGFub3RhZG9zCi0gUG9yIGNhZGEgZ29sIGFub3RhZG8sIGVsIHByZWNpbyBkZWwganVnYWRvciBhdW1lbnRhIGVuIOKCrDg5MCw1MzU7IG11Y2hvIG1lbm9yIGFsIGVzdGltYWRvIHBvciBlbCBtw6l0b2RvIGNsw6FzaWNvIGRlIOKCrDMsMjY3LDM3NQotIEVsIGludGVyY2VwdG8gZXMg4oKsMywwNDAsODI0LCBxdWUgcmVwcmVzZW50YSBlbCBwcmVjaW8gYmFzZSBlc3RpbWFkbyBwYXJhIHVuIGp1Z2Fkb3Igc2luIGdvbGVzIHRhbWJpw6luIHByZXNlbnRlIHVuYSBub3RhYmxlIGJhamEgZnJlbnRlIGFsIGRlbCBtb2RlbG8gY2zDoXNpY28gZGUg4oKsNSwyMzgsNDUyCi0gRWwgUsKyIGFqdXN0YWRvIGVzIDAuMjE3NywgbG8gcXVlIGluZGljYSBxdWUgZWwgbW9kZWxvIGV4cGxpY2EgYXByb3hpbWFkYW1lbnRlIGVsIDIxLjglIGRlIGxhIHZhcmlhYmlsaWRhZCBlbiBsb3MgcHJlY2lvcywgb2JzZXJ2YW5kbyB1bmEgZGlzbXVuaWNpw7NuIGNvbnNpZGVyYWJsZSBmcmVudGUgYWwgMzAlIGRlbCBtb2RlbG8gY2zDoXNpY28uCgojIyMgRXZhbHVhY2nDs24geSBjb21wYXJhY2nDs24gZGUgbW9kZWxvcyBmcmVudGUgYSBkYXRvcyBkZSBwcnVlYmEKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2RlbG9zX3NpbXBsZXMgPC0gbGlzdChzaW1wbGVfMSA9IG1vZGVsb19jbGFzaWNvX2dvbGVzLCAKICAgICAgICAgICAgICAgIHNpbXBsZV8yID0gbW9kZWxvX2xtcm9iX2dvbGVzKQoKbGlzdGFfcHJlZGljY2lvbmVzX3Rlc3RpbmcgPSBtYXAoLnggPSBtb2RlbG9zX3NpbXBsZXMsIC5mID0gYXVnbWVudCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkgCgoKZ29sZXNfY2xhc2ljb190ZXN0ID0gbGlzdGFfcHJlZGljY2lvbmVzX3Rlc3Rpbmckc2ltcGxlXzEgJT4lICAKICBtZXRyaWNzKHRydXRoPXByZWNpbywgZXN0aW1hdGU9LmZpdHRlZCkgJT4lCiAgbXV0YXRlKC5lc3RpbWF0ZT1yb3VuZCguZXN0aW1hdGUsIDQpKQoKZ29sZXNfcm9idXN0b190ZXN0ID0gbGlzdGFfcHJlZGljY2lvbmVzX3Rlc3Rpbmckc2ltcGxlXzIgJT4lICAKICBtZXRyaWNzKHRydXRoPXByZWNpbywgZXN0aW1hdGU9LmZpdHRlZCkgJT4lCiAgbXV0YXRlKC5lc3RpbWF0ZT1yb3VuZCguZXN0aW1hdGUsIDQpKQoKCm1ldHJpY2FfZ29sZXMgPC0gcmJpbmQoZ29sZXNfY2xhc2ljb190ZXN0W2MoMSwzKSxdLCBnb2xlc19yb2J1c3RvX3Rlc3RbYygxLDMpLF0pCgptb2RlbGl0b3Nfc2ltcGxlcyA8LSBjKHJlcCgiQ2xhc2ljbyAtIEdvbGVzIiwyKSxyZXAoIlJvYnVzdG8gLSBHb2xlcyIsMikpCm1ldHJpY2FzIDwtIGNiaW5kKG1vZGVsaXRvc19zaW1wbGVzLCBtZXRyaWNhX2dvbGVzKQprYWJsZShtZXRyaWNhcykKYGBgCgotIEVsIFJNU0UgZXMgbWVub3IgcGFyYSBlbCBtb2RlbG8gY2zDoXNpY28sIHNpbiBlbWJhcmdvLCBhbCBlc3RhciB0cmFiYWphbmRvIGNvbiBkYXRvcyBxdWUgdGllbmVuIG91dGxpZXJzIGxvIGNvcnJlY3RvIGVzIGNvbXBhcmFybG8gY29udHJhIGxvcyB2YWxvcmVzIGRlIE1BRS4KLSBFbCBNQUUgZXMgbWVub3IgcGFyYSBlbCBtb2RlbG8gcm9idXN0bywgbW9zdHJhbmRvIHVuIG1lam9yIGRlc2VtcGXDsW8gZnJlbnRlIGFsIG1vZGVsbyBjbMOhc2ljby4KCiMjIFByaW1lcm8gTW9kZWxvIE11bHRpcGxlIFJvYnVzdG8KCiRsb2coUHJlY2lvKSQgPSAkR29sZXMkICsgJEVkYWQkICsgJEVkYWReMiQgKyAkQXNpc3RlbmNpYXMkICsgJENvbnRpbmVudGUkICRkZSQgJG5hY2ltaWVudG8kICRkZWwkICRqdWdhZG9yJCArICRMaWdhJCAkZG9uZGUkICRqdWVnYSQKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2RlbG9fbXVsdGlwbGVfbG1yb2JfMiA8LSBsbXJvYihmb3JtdWxhID0gbG9nKHByZWNpbykgfiBHbHMgKyBBZ2UgKyBJKEFnZV4yKSArIEFzdCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250aW5lbnRlICsgY3VycmVudF9jbHViX2RvbWVzdGljX2NvbXBldGl0aW9uX2lkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9dHJhaW5fZGF0YSkKc3VtbWFyeShtb2RlbG9fbXVsdGlwbGVfbG1yb2JfMikKYGBgCgojIyMjIEludGVycHJldGFjacOzbiBkZWwgTW9kZWxvCi0gRWwgbW9kZWxvIHV0aWxpemEgbGEgdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYSBkZSBsYSB2YXJpYWJsZSBwcmVjaW8geSBtYW50aWVuZSBsYXMgbWlzbWFzIHZhcmlhYmxlcyBwcmVkaWN0b3JhcwotIExvcyBjb2VmaWNpZW50ZXMgbcOhcyBzaWduaWZpY2F0aXZvcyBzb246CiAgLSBHb2xlczogKzAuMTExIChwLXZhbHVlIDwgMmUtMTYpLiBFc3RvIGluZGljYSBxdWUgZWwgcHJlY2lvIGRlIGxvcyBqdWdhZG9yZXMgcXVlIHRpZW5lbiBtaXNtYSBjYW50aWRhZCBkZSBhc2lzdGVuY2lhcyB5IGVkYWQgYXVtZW50YSB1biAxMS43JSBwb3IgY2FkYSBnb2wgYW5vdGFkby4gCiAgLSBBc2lzdGVuY2lhczogKzAuMTcwIChwLXZhbHVlIDwgMmUtMTYpLiBFc3RvIGluZGljYSBxdWUgZWwgcHJlY2lvIGRlIGxvcyBqdWdhZG9yZXMgcXVlIHRpZW5lbiBtaXNtYSBjYW50aWRhZCBkZSBnb2xlcyB5IGVkYWQgYXVtZW50YSB1biAxOC41JSBwb3IgY2FkYSBhc2lzdGVuY2lhIGFkaWNpb25hbC4KICAtIEVkYWQ6IGVmZWN0byBjdWFkcsOhdGljbyBzaWduaWZpY2F0aXZvIChwLXZhbHVlIDwgMmUtMTYpCiAgLSBBbcOpcmljYTogKzAuNDQwIHJlc3BlY3RvIGEgw4FmcmljYSAocC12YWx1ZSA9IDAuMDAwOCksIGluZGljYSB1biBhdW1lbnRvIGVzdGFkw61zdGljYW1ldGUgc2lnbmlmaWNhdGl2byBkZSA1NS4yJSBlbiBlbCBwcmVjaW8gZGUgbG9zIGp1Z2Fkb3JlcyBhbWVyaWNhbm9zIGNvbiByZXNwZWN0byBhIGxvcyBhZnJpY2Fub3MuIEVzIG1heW9yIGNvbiByZXNwZWN0byBhbCBtb2RlbG8gY2zDoXNpY28sIGNvbiBsbyBjdWFsIGFsIHF1aXRhcmxlIHBlc28gYSBsb3MganVnYWRvcmVzIG1hcyBjYXJvcyBwb2RlbW9zIGRlY2lyIHF1ZSBsb3MganVnYWRvcmVzIGFtZXJpY2Fub3Mgc29uIGHDum4gbWFzIGFwcmVjaWFkb3MgcXVlIGxvcyBhZnJpY2Fub3MuCiAgLSBQcmVtaWVyIExlYWd1ZTogKzAuOTE3IHJlc3BlY3RvIGEgbGEgbGlnYSBkZSByZWZlcmVuY2lhLCAocC12YWx1ZSA8IDMuNjZlLTE2KSBpbmRpY2EgdW4gYXVtZW50byBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZvIGRlIDE1MC4yJSBlbiBlbCBwcmVjaW8gZGUgbG9zIGp1Z2Fkb3JlcyBkZSBsYSBQcmVtaWVyIExlYWd1ZSBjb24gcmVzcGVjdG8gYSBsb3MganVnYWRvcmVzIGRlIExhIExpZ2EgKEVzcGHDsWEpCiAgLSBMaWd1ZSAxIChGcmFuY2lhKTogLTAuMjMxIHJlc3BlY3RvIGEgbGEgbGlnYSBkZSByZWZlcmVuY2lhLCAocC12YWx1ZSA9IDAuMDMpIGluZGljYSB1bmEgZGlzbWludWNpw7NuIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdmEgZGUgMjAuNiUgZW4gZWwgcHJlY2lvIGRlIGxvcyBqdWdhZG9yZXMgZGUgbGEgTGlndWUgMSBjb24gcmVzcGVjdG8gYSBsb3MganVnYWRvcmVzIGRlIExhIExpZ2EuCiAgLSBCdW5kZXNsaWdhOiAtMC4yODIgcmVzcGVjdG8gZGUgbGEgbGlnYSBkZSByZWZlcmVuY2lhIChwLXZhbHVlID0gMC4wMDQpIGluZGljYSB1bmEgZGlzbWludWNpw7NuIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdmEgZGUgMjQuNiUgZW4gZWwgcHJlaWNpbyBkZSBsb3MganVnYWRvcmVzIGRlIGxhIEJ1bmRlc2xpZ2EgY29uIHJlc3BlY3RvIGEgTGEgTGlnYS4KLSBFbCBSwrIgYWp1c3RhZG8gZXMgMC40NzkzLCBzaW1pbGFyIGFsIG1vZGVsbyBhbnRlcmlvciBwZXJvIGNvbiBtZWpvciBpbnRlcnByZXRhYmlsaWRhZCwgYXVtZW50YW5kbyB1biAxLjc0JSBjb24gcmVzcGVjdG8gYWwgbcOzZGVsbyBjbMOhc2ljby4KCiMgRVNUTyBOTyBTRSBTSSBDT1JSRVNQT05ERSBQT05FUkxPIFBPUlFVRSBBTCBTRVIgVU5BIEZPUk1BIE5PIFBBUkFNw4lUUklDQSBDUkVPIFFVRSBOTyBDT1JSRVNQT05ERQojIyMgRGlhZ27Ds3N0aWNvCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI0RpYWduw7NzdGljbwpkYXRvc19hdWdtZW50YWRvcyA8LSBhdWdtZW50KG1vZGVsb19tdWx0aXBsZV9sbXJvYl8yKQpkYXRvc19hdWdtZW50YWRvcyQuc3RkLnJlc2lkIDwtIGRhdG9zX2F1Z21lbnRhZG9zJC5yZXNpZC8obW9kZWxvX211bHRpcGxlX2xtcm9iXzIkc2NhbGUqc3FydCgxLWhhdHZhbHVlcyhtb2RlbG9fbXVsdGlwbGVfbG1yb2JfMikpKQpkYXRvc19hdWdtZW50YWRvcyQuaGF0IDwtIGhhdHZhbHVlcyhtb2RlbG9fbXVsdGlwbGVfbG1yb2JfMikKZzEzIDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKC5maXR0ZWQsIC5yZXNpZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArCiAgbGFicyh0aXRsZSA9ICJSZXNpZHVvcyB2cyB2YWxvcmVzIHByZWRpY2hvcyIpICsgCiAgdGhlbWVfYncoKQpnMTQgPC0gZ2dwbG90KGRhdG9zX2F1Z21lbnRhZG9zLCBhZXMoc2FtcGxlID0gLnN0ZC5yZXNpZCkpICsKICBzdGF0X3FxKCkgKwogIGdlb21fYWJsaW5lKCkgKwogIGxhYnModGl0bGUgPSAiTm9ybWFsIFFRIHBsb3QiKSArIAogIHRoZW1lX2J3KCkKZzE1IDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKC5maXR0ZWQsIHNxcnQoYWJzKC5zdGQucmVzaWQpKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh0aXRsZSA9ICJTY2FsZS1sb2NhdGlvbiBwbG90IikKZzE2IDwtIGdncGxvdChkYXRvc19hdWdtZW50YWRvcywgYWVzKC5oYXQsIC5zdGQucmVzaWQpKSArCiAgZ2VvbV92bGluZShzaXplID0gMiwgY29sb3VyID0gIndoaXRlIiwgeGludGVyY2VwdCA9IDApICsKICBnZW9tX2hsaW5lKHNpemUgPSAyLCBjb2xvdXIgPSAid2hpdGUiLCB5aW50ZXJjZXB0ID0gMCkgKwogIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsgCiAgdGhlbWVfYncoKSArCiAgbGFicyh0aXRsZSA9ICJSZXNpZHVhbCB2cyBsZXZlcmFnZSIpCmdyaWQuYXJyYW5nZShnMTMsIGcxNCwgZzE1LCBnMTYsIG5yb3cgPSAyKQpgYGAKCiMjIyBFdmFsdWFjacOzbiB5IGNvbXBhcmFjacOzbiBkZSBtb2RlbG9zIGZyZW50ZSBhIGRhdG9zIGRlIHBydWViYQoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1vZGVsb3NfY29tcGFyYWNpb24gPC0gbGlzdChtdWx0aXBsZV8yID0gbW9kZWxvX2NsYXNpY29fbXVsdGlwbGUsCiAgICAgICAgICAgICAgICByb2J1c3RvXzIgPSBtb2RlbG9fbXVsdGlwbGVfbG1yb2JfMikKCmxpc3RhX3ByZWRpY2Npb25lc190ZXN0aW5nID0gbWFwKC54ID0gbW9kZWxvc19jb21wYXJhY2lvbiwgLmYgPSBhdWdtZW50LCBuZXdkYXRhID0gdGVzdF9kYXRhKSAKCgptZXRyaWNhczJfdGVzdCA9IGxpc3RhX3ByZWRpY2Npb25lc190ZXN0aW5nJG11bHRpcGxlXzIgJT4lICAKICBtdXRhdGUoZXhwX2ZpdHRlZD0gZXhwKC5maXR0ZWQpKSAlPiUgCiAgbWV0cmljcyh0cnV0aD1wcmVjaW8sIGVzdGltYXRlPWV4cF9maXR0ZWQpICU+JQogIG11dGF0ZSguZXN0aW1hdGU9cm91bmQoLmVzdGltYXRlLCA0KSkKCm1ldHJpY2FzM190ZXN0ID0gbGlzdGFfcHJlZGljY2lvbmVzX3Rlc3Rpbmckcm9idXN0b18yICU+JSAKICBtdXRhdGUoZXhwX2ZpdHRlZD0gZXhwKC5maXR0ZWQpKSAlPiUKICBtZXRyaWNzKHRydXRoPXByZWNpbywgZXN0aW1hdGU9ZXhwX2ZpdHRlZCkgJT4lCiAgbXV0YXRlKC5lc3RpbWF0ZT1yb3VuZCguZXN0aW1hdGUsIDQpKQoKCm1ldHJpY2EgPC0gcmJpbmQobWV0cmljYXMyX3Rlc3RbYygxLDMpLF0sIG1ldHJpY2FzM190ZXN0W2MoMSwzKSxdKQoKbW9kZWxpdG9zX2NvbXBhcmFjaW9uIDwtIGMocmVwKCJNdWx0aXBsZSAtIGxvZyhQcmVjaW8pIiwyKSwKICAgICAgICAgICAgICAgcmVwKCJSb2J1c3RvIC0gbG9nKFByZWNpbykgLSBwc2kgPSBiaXNxdWFyZSIsMikpCm1ldHJpY2FzIDwtIGNiaW5kKG1vZGVsaXRvc19jb21wYXJhY2lvbiwgbWV0cmljYSkKa2FibGUobWV0cmljYXMpCmBgYAoKLSBFbCBSTVNFIGVzIG1lbm9yIHBhcmEgZWwgbW9kZWxvIGNsw6FzaWNvLCBzaW4gZW1iYXJnbywgYWwgZXN0YXIgdHJhYmFqYW5kbyBjb24gZGF0b3MgcXVlIHRpZW5lbiBvdXRsaWVycyBsbyBjb3JyZWN0byBlcyBjb21wYXJhcmxvIGNvbnRyYSBsb3MgdmFsb3JlcyBkZSBNQUUuCi0gRWwgTUFFIGVzIG1lbm9yIHBhcmEgZWwgbW9kZWxvIHJvYnVzdG8sIG1vc3RyYW5kbyB1biBtZWpvciBkZXNlbXBlw7FvIGZyZW50ZSBhbCBtb2RlbG8gY2zDoXNpY28uCgojIyBNb2RlbG9zIFJvYnVzdG9zIGNvbiBvdHJhcyBmdW5jaW9uZXMgJFxyaG8kCgpTZSBhcGxpY2EgbGEgbWlzbWEgZsOzcm11bGEgcXVlIGVsIG1vZGVsbyBhbnRlcmlvciBwZXJvIGVuIGx1Z2FyIGRlIHVzYXIgbGEgZnVuY2nDs24gJFxwc2kkIHBvciBkZWZlY3RvIGJpc3F1ZWFyZSBoYWNlbW9zIDUgbW9kZWxvcyBudWV2b3MgdXNhbmRvIGVuIGNhZGEgdW5vIGRpZmVyZW50ZXMgJFxyaG8kOiBscXEsIHdlbHNoLCBvcHRpbWFsLCBoYW1wZWwgeSBnZ3cuCgojIyMgRXZhbHVhY2nDs24gZGUgdG9kb3MgbG9zIG1vZGVsb3MKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2RlbG9fbXVsdGlwbGVfbG1yb2JfbHFxIDwtIGxtcm9iKGZvcm11bGEgPSBsb2cocHJlY2lvKSB+IEdscyArIEFnZSArIEkoQWdlXjIpICsgQXN0ICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGluZW50ZSArIGN1cnJlbnRfY2x1Yl9kb21lc3RpY19jb21wZXRpdGlvbl9pZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9dHJhaW5fZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHNpID0gImxxcSIpCgptb2RlbG9fbXVsdGlwbGVfbG1yb2Jfd2Vsc2ggPC0gbG1yb2IoZm9ybXVsYSA9IGxvZyhwcmVjaW8pIH4gR2xzICsgQWdlICsgSShBZ2VeMikgKyBBc3QgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbmVudGUgKyBjdXJyZW50X2NsdWJfZG9tZXN0aWNfY29tcGV0aXRpb25faWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9dHJhaW5fZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwc2kgPSAid2Vsc2giKQoKbW9kZWxvX211bHRpcGxlX2xtcm9iX29wdGltYWwgPC0gbG1yb2IoZm9ybXVsYSA9IGxvZyhwcmVjaW8pIH4gR2xzICsgQWdlICsgSShBZ2VeMikgKyBBc3QgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGluZW50ZSArIGN1cnJlbnRfY2x1Yl9kb21lc3RpY19jb21wZXRpdGlvbl9pZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPXRyYWluX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwc2kgPSAib3B0aW1hbCIpCgoKbW9kZWxvX211bHRpcGxlX2xtcm9iX2hhbXBlbCA8LSBsbXJvYihmb3JtdWxhID0gbG9nKHByZWNpbykgfiBHbHMgKyBBZ2UgKyBJKEFnZV4yKSArIEFzdCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbmVudGUgKyBjdXJyZW50X2NsdWJfZG9tZXN0aWNfY29tcGV0aXRpb25faWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPXRyYWluX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzaSA9ICJoYW1wZWwiKQoKbW9kZWxvX211bHRpcGxlX2xtcm9iX2dndyA8LSBsbXJvYihmb3JtdWxhID0gbG9nKHByZWNpbykgfiBHbHMgKyBBZ2UgKyBJKEFnZV4yKSArIEFzdCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbmVudGUgKyBjdXJyZW50X2NsdWJfZG9tZXN0aWNfY29tcGV0aXRpb25faWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPXRyYWluX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzaSA9ICJnZ3ciKQoKCm1vZGVsb3MgPC0gbGlzdChtdWx0aXBsZV8xID0gbW9kZWxvX2NsYXNpY29fbXVsdGlwbGVfMSwgCiAgICAgICAgICAgICAgICBtdWx0aXBsZV8yID0gbW9kZWxvX2NsYXNpY29fbXVsdGlwbGUsCiAgICAgICAgICAgICAgICByb2J1c3RvXzIgPSBtb2RlbG9fbXVsdGlwbGVfbG1yb2JfMiwKICAgICAgICAgICAgICAgIHJvYnVzdG9fMyA9IG1vZGVsb19tdWx0aXBsZV9sbXJvYl9scXEsCiAgICAgICAgICAgICAgICByb2J1c3RvXzQgPSBtb2RlbG9fbXVsdGlwbGVfbG1yb2Jfd2Vsc2gsCiAgICAgICAgICAgICAgICByb2J1c3RvXzUgPSBtb2RlbG9fbXVsdGlwbGVfbG1yb2Jfb3B0aW1hbCwKICAgICAgICAgICAgICAgIHJvYnVzdG9fNiA9IG1vZGVsb19tdWx0aXBsZV9sbXJvYl9oYW1wZWwsCiAgICAgICAgICAgICAgICByb2J1c3RvXzcgPSBtb2RlbG9fbXVsdGlwbGVfbG1yb2JfZ2d3KQoKbGlzdGFfcHJlZGljY2lvbmVzX3Rlc3RpbmcgPSBtYXAoLnggPSBtb2RlbG9zLCAuZiA9IGF1Z21lbnQsIG5ld2RhdGEgPSB0ZXN0X2RhdGEpIAoKCm1ldHJpY2FzMV90ZXN0ID0gbGlzdGFfcHJlZGljY2lvbmVzX3Rlc3RpbmckbXVsdGlwbGVfMSAlPiUgIAogIG1ldHJpY3ModHJ1dGg9cHJlY2lvLCBlc3RpbWF0ZT0uZml0dGVkKSAlPiUKICBtdXRhdGUoLmVzdGltYXRlPXJvdW5kKC5lc3RpbWF0ZSwgNCkpCgptZXRyaWNhczJfdGVzdCA9IGxpc3RhX3ByZWRpY2Npb25lc190ZXN0aW5nJG11bHRpcGxlXzIgJT4lICAKICBtdXRhdGUoZXhwX2ZpdHRlZD0gZXhwKC5maXR0ZWQpKSAlPiUgCiAgbWV0cmljcyh0cnV0aD1wcmVjaW8sIGVzdGltYXRlPWV4cF9maXR0ZWQpICU+JQogIG11dGF0ZSguZXN0aW1hdGU9cm91bmQoLmVzdGltYXRlLCA0KSkKCm1ldHJpY2FzM190ZXN0ID0gbGlzdGFfcHJlZGljY2lvbmVzX3Rlc3Rpbmckcm9idXN0b18yICU+JSAKICBtdXRhdGUoZXhwX2ZpdHRlZD0gZXhwKC5maXR0ZWQpKSAlPiUKICBtZXRyaWNzKHRydXRoPXByZWNpbywgZXN0aW1hdGU9ZXhwX2ZpdHRlZCkgJT4lCiAgbXV0YXRlKC5lc3RpbWF0ZT1yb3VuZCguZXN0aW1hdGUsIDQpKQoKbWV0cmljYXM0X3Rlc3QgPSBsaXN0YV9wcmVkaWNjaW9uZXNfdGVzdGluZyRyb2J1c3RvXzMgJT4lICAKICBtdXRhdGUoZXhwX2ZpdHRlZD0gZXhwKC5maXR0ZWQpKSAlPiUgCiAgbWV0cmljcyh0cnV0aD1wcmVjaW8sIGVzdGltYXRlPWV4cF9maXR0ZWQpICU+JQogIG11dGF0ZSguZXN0aW1hdGU9cm91bmQoLmVzdGltYXRlLCA0KSkKCm1ldHJpY2FzNV90ZXN0ID0gbGlzdGFfcHJlZGljY2lvbmVzX3Rlc3Rpbmckcm9idXN0b180ICU+JSAgCiAgbXV0YXRlKGV4cF9maXR0ZWQ9IGV4cCguZml0dGVkKSkgJT4lIAogIG1ldHJpY3ModHJ1dGg9cHJlY2lvLCBlc3RpbWF0ZT1leHBfZml0dGVkKSAlPiUKICBtdXRhdGUoLmVzdGltYXRlPXJvdW5kKC5lc3RpbWF0ZSwgNCkpCgptZXRyaWNhczZfdGVzdCA9IGxpc3RhX3ByZWRpY2Npb25lc190ZXN0aW5nJHJvYnVzdG9fNSAlPiUgIAogIG11dGF0ZShleHBfZml0dGVkPSBleHAoLmZpdHRlZCkpICU+JSAKICBtZXRyaWNzKHRydXRoPXByZWNpbywgZXN0aW1hdGU9ZXhwX2ZpdHRlZCkgJT4lCiAgbXV0YXRlKC5lc3RpbWF0ZT1yb3VuZCguZXN0aW1hdGUsIDQpKQoKbWV0cmljYXM3X3Rlc3QgPSBsaXN0YV9wcmVkaWNjaW9uZXNfdGVzdGluZyRyb2J1c3RvXzYgJT4lICAKICBtdXRhdGUoZXhwX2ZpdHRlZD0gZXhwKC5maXR0ZWQpKSAlPiUgCiAgbWV0cmljcyh0cnV0aD1wcmVjaW8sIGVzdGltYXRlPWV4cF9maXR0ZWQpICU+JQogIG11dGF0ZSguZXN0aW1hdGU9cm91bmQoLmVzdGltYXRlLCA0KSkKCm1ldHJpY2FzOF90ZXN0ID0gbGlzdGFfcHJlZGljY2lvbmVzX3Rlc3Rpbmckcm9idXN0b183ICU+JSAgCiAgbXV0YXRlKGV4cF9maXR0ZWQ9IGV4cCguZml0dGVkKSkgJT4lIAogIG1ldHJpY3ModHJ1dGg9cHJlY2lvLCBlc3RpbWF0ZT1leHBfZml0dGVkKSAlPiUKICBtdXRhdGUoLmVzdGltYXRlPXJvdW5kKC5lc3RpbWF0ZSwgNCkpCgoKCm1ldHJpY2FzIDwtIHJiaW5kKG1ldHJpY2FzMV90ZXN0W2MoMSwzKSxdLCBtZXRyaWNhczJfdGVzdFtjKDEsMyksXSkKbWV0cmljYXMgPC0gcmJpbmQobWV0cmljYXMsIG1ldHJpY2FzM190ZXN0W2MoMSwzKSxdKQptZXRyaWNhcyA8LSByYmluZChtZXRyaWNhcywgbWV0cmljYXM0X3Rlc3RbYygxLDMpLF0pCm1ldHJpY2FzIDwtIHJiaW5kKG1ldHJpY2FzLCBtZXRyaWNhczVfdGVzdFtjKDEsMyksXSkKbWV0cmljYXMgPC0gcmJpbmQobWV0cmljYXMsIG1ldHJpY2FzNl90ZXN0W2MoMSwzKSxdKQptZXRyaWNhcyA8LSByYmluZChtZXRyaWNhcywgbWV0cmljYXM3X3Rlc3RbYygxLDMpLF0pCm1ldHJpY2FzIDwtIHJiaW5kKG1ldHJpY2FzLCBtZXRyaWNhczhfdGVzdFtjKDEsMyksXSkKCm1vZGVsaXRvcyA8LSBjKHJlcCgiTXVsdGlwbGUgLSBQcmVjaW8iLDIpLAogICAgICAgICAgICAgICByZXAoIk11bHRpcGxlIC0gbG9nKFByZWNpbykiLDIpLAogICAgICAgICAgICAgICByZXAoIlJvYnVzdG8gLSBsb2coUHJlY2lvKSAtIHBzaSA9IGJpc3FhcmUiLDIpLAogICAgICAgICAgICAgICByZXAoIlJvYnVzdG8gLSBsb2coUHJlY2lvKSAtIHBzaSA9IGxxcSIsMiksCiAgICAgICAgICAgICAgIHJlcCgiUm9idXN0byAtIGxvZyhQcmVjaW8pIC0gcHNpID0gd2Vsc2giLDIpLCAKICAgICAgICAgICAgICAgcmVwKCJSb2J1c3RvIC0gbG9nKFByZWNpbykgLSBwc2kgPSBvcHRpbWFsIiwyKSwKICAgICAgICAgICAgICAgcmVwKCJSb2J1c3RvIC0gbG9nKFByZWNpbykgLSBwc2kgPSBoYW1wZWwiLDIpLAogICAgICAgICAgICAgICByZXAoIlJvYnVzdG8gLSBsb2coUHJlY2lvKSAtIHBzaSA9IGdndyIsMikpCm1ldHJpY2FzIDwtIGNiaW5kKG1vZGVsaXRvcywgbWV0cmljYXMpCmthYmxlKG1ldHJpY2FzKQpgYGAKCi0gRWwgTUFFIGRlIHRvZG9zIGxvcyBtb2RlbG9zIHJvYnVzdG9zIGVzIG1lbm9yIHF1ZSBlbCBkZSBsb3MgbW9kZWxvcyBjbMOhc2ljb3MKLSBFbCBtb2RlbG8gcXVlIHV0aWxpemEgbGEgZnVuY2nDs24gJFxyaG8kIG9wdGltYWwgdGllbmUgbGUgbWVub3IgTUFFLCBzaWVuZG8gZWwgZGUgbWVqb3IgZGVzZW1wZcOxby4gRXN0byBlcyBwb3NpYmxlIHlhIHlhIHF1ZSBsYSBmdW5jaW9uIG9wdGltYWwgZXMgYXByb3BpYWRhIHBhcmEgZGF0b3MgZG9uZGUgbG9zIG91dGxpZXJzIG5vIHNvbiBtdXkgcHJvbnVuY2lhZG9zLCBxdWUgcGFyZWNlIHNlciBlbCBjYXNvIGRlbCBwcmVjaW8gZGUgbG9zIGp1Z2Fkb3Jlcy4KCgoKIyBDb25jbHVzaW9uZXMKCkVsIGFuw6FsaXNpcyBlc3RhZMOtc3RpY28gcmV2ZWxhIHBhdHJvbmVzIGZ1bmRhbWVudGFsZXMgZW4gbGEgdmFsb3JhY2nDs24gZGUganVnYWRvcmVzIHF1ZSBkZXNhZsOtYW4gdmFyaWFzIHBlcmNlcGNpb25lcyB0cmFkaWNpb25hbGVzIGRlbCBtZXJjYWRvLiAKTGEgZXZpZGVuY2lhIGVtcMOtcmljYSBkZW11ZXN0cmEgdW5hIGNsYXJhIHNlZ21lbnRhY2nDs24gZGVsIG1lcmNhZG8gZXVyb3BlbywgY29uIGluZWZpY2llbmNpYXMgc2lnbmlmaWNhdGl2YXMgcXVlIGNyZWFuIG9wb3J0dW5pZGFkZXMgZXN0cmF0w6lnaWNhcy4KCkxhIGVsZWNjacOzbiBtZXRvZG9sw7NnaWNhIGRlIHV0aWxpemFyIHJlZ3Jlc2nDs24gcm9idXN0YSBjb24gdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYSBkZW1vc3Ryw7Mgc2VyIGNydWNpYWwgcGFyYSBsYSB2YWxpZGV6IGRlbCBhbsOhbGlzaXMuIApFbCBtw6l0b2RvIE1NLWVzdGltYXRpb24gaW1wbGVtZW50YWRvIGVuIFJvYnVzdGJhc2UsIHF1ZSB1dGlsaXphIHBvciBkZWZlY3RvIGxhIGZ1bmNpw7NuIGJpc3F1YXJlLCBwZXJtaXRpw7MgbWFuZWphciBlZmljYXptZW50ZSBsYSBoZXRlcm9nZW5laWRhZCBpbmhlcmVudGUgYWwgbWVyY2FkbyBkZSBmaWNoYWplcywgZG9uZGUgbGFzIHZhbG9yYWNpb25lcyBleHRyZW1hcyBzb24gY29tdW5lcyBwZXJvIG5vIG5lY2VzYXJpYW1lbnRlIG91dGxpZXJzIGVzdGFkw61zdGljb3MuIApMYSB0cmFuc2Zvcm1hY2nDs24gbG9nYXLDrXRtaWNhIG5vIHNvbG8gbWVqb3LDsyBsYXMgcHJvcGllZGFkZXMgZXN0YWTDrXN0aWNhcyBkZWwgbW9kZWxvLCBzaW5vIHF1ZSB0YW1iacOpbiBwcm9wb3JjaW9uw7MgdW5hIGludGVycHJldGFjacOzbiBtw6FzIGludHVpdGl2YSBlbiB0w6lybWlub3MgZGUgdmFyaWFjaW9uZXMgcG9yY2VudHVhbGVzLCBhbGluZcOhbmRvc2UgbmF0dXJhbG1lbnRlIGNvbiBsYSBmb3JtYSBlbiBxdWUgZWwgbWVyY2FkbyBldmFsw7phIGxvcyBjYW1iaW9zIGVuIGVsIHZhbG9yIGRlIGxvcyBqdWdhZG9yZXMuIApFbCB1c28gZGUgZXN0aW1hZG9yZXMgcm9idXN0b3MgcmV2ZWzDsyBxdWUgZWwgbWVyY2FkbyBkZSBmaWNoYWplcywgYXVucXVlIHZvbMOhdGlsLCBtYW50aWVuZSB1bmEgZXN0cnVjdHVyYSBzdWJ5YWNlbnRlIHF1ZSBwdWVkZSBzZXIgbW9kZWxhZGEgZGUgbWFuZXJhIG3DoXMgcHJlY2lzYSBjdWFuZG8gc2UgdXRpbGl6YW4gbcOpdG9kb3MgcXVlIGVxdWlsaWJyYW4gcm9idXN0ZXogeSBlZmljaWVuY2lhIGVzdGFkw61zdGljYS4KCkVsIG1vZGVsbyByb2J1c3RvLCBjb24gdW4gUsKyIGFqdXN0YWRvIGRlIDQ3LjkzJSwgY2FwdHVyYSBsYXMgZGluw6FtaWNhcyBmdW5kYW1lbnRhbGVzIGRlbCBtZXJjYWRvIG1pZW50cmFzIGZpbHRyYSBlbCAicnVpZG8iIGVzcGVjdWxhdGl2by4gCkxhIGVmZWN0aXZpZGFkIGRlIGxhIGZ1bmNpw7NuIGJpc3F1YXJlIHN1Z2llcmUgcXVlLCBjb250cmFyaW8gYSBsYSBwZXJjZXBjacOzbiBwb3B1bGFyLCBsYXMgdmFsb3JhY2lvbmVzIGV4dHJlbWFzIGVuIGVsIG1lcmNhZG8gc2lndWVuIHBhdHJvbmVzIGlkZW50aWZpY2FibGVzIHkgbm8gc29uIHB1cmFtZW50ZSBlc3BlY3VsYXRpdmFzLgoKTGEgUHJlbWllciBMZWFndWUgbWFudGllbmUgdW5hIHByaW1hIGRlIHZhbG9yYWNpw7NuIGRlbCAxNTAuMiUgc29icmUgTGEgTGlnYSwgdW4gZGlmZXJlbmNpYWwgcXVlIGV4Y2VkZSBzaWduaWZpY2F0aXZhbWVudGUgbGFzIGRpZmVyZW5jaWFzIGVuIGluZ3Jlc29zIG9wZXJhdGl2b3MgZW50cmUgZXN0YXMgbGlnYXMuIApFc3RhIHNvYnJldmFsb3JhY2nDs24gc2lzdGVtw6F0aWNhIHN1Z2llcmUgdW5hIGJ1cmJ1amEgZXN0cnVjdHVyYWwgZW4gZWwgbWVyY2FkbyBpbmdsw6lzLCBwYXJ0aWN1bGFybWVudGUgbm90YWJsZSBlbiBjb21wYXJhY2nDs24gY29uIEJ1bmRlc2xpZ2EgKC0yNC42JSkgeSBMaWd1ZSAxICgtMjAuNiUpLCBkb25kZSBlbCB0YWxlbnRvIHBhcmVjZSBlc3RhciBzaXN0ZW3DoXRpY2FtZW50ZSBpbmZyYXZhbG9yYWRvLgoKVW5hIG9ic2VydmFjacOzbiBwYXJ0aWN1bGFybWVudGUgcmVsZXZhbnRlIGVzIGxhIG1heW9yIHZhbG9yYWNpw7NuIGRlIGxhcyBhc2lzdGVuY2lhcyAoKzE4LjUlKSBzb2JyZSBsb3MgZ29sZXMgKCsxMS43JSkuIApFc3RhIGRpZmVyZW5jaWEgcmVmbGVqYSB1bmEgZXZvbHVjacOzbiBlbiBsYSBjb21wcmVuc2nDs24gZGVsIHZhbG9yIGNyZWF0aXZvIGVuIGVsIGbDunRib2wgbW9kZXJubywgZG9uZGUgbGEgY2FwYWNpZGFkIGRlIGdlbmVyYXIgb3BvcnR1bmlkYWRlcyBzZSBwcmVtaWEgcG9yIGVuY2ltYSBkZSBsYSBmaW5hbGl6YWNpw7NuLiAKRXN0ZSBwYXRyw7NuIHN1Z2llcmUgdW5hIHNvZmlzdGljYWNpw7NuIGNyZWNpZW50ZSBlbiBsYSBldmFsdWFjacOzbiBkZWwgdGFsZW50by4KCkxhIHJlbGFjacOzbiBjdWFkcsOhdGljYSBjb24gbGEgZWRhZCBlbWVyZ2UgY29tbyB1biBmYWN0b3IgY3LDrXRpY28gZW4gbGEgdmFsb3JhY2nDs24sIHNlw7FhbGFuZG8gdW5hIHZlbnRhbmEgw7NwdGltYSBkZSB2YWxvcmFjacOzbiBxdWUgZWwgbWVyY2FkbyByZWNvbm9jZSBjb25zaXN0ZW50ZW1lbnRlLiAKRXN0ZSBwYXRyw7NuIHRpZW5lIGltcGxpY2FjaW9uZXMgcHJvZnVuZGFzIHBhcmEgbGEgZ2VzdGnDs24gZGUgYWN0aXZvcyBkZXBvcnRpdm9zIHkgbGEgcGxhbmlmaWNhY2nDs24gZGUgcGxhbnRpbGxhcyBhIGxhcmdvIHBsYXpvLgoKTGEgYnJlY2hhIGRlIHZhbG9yYWNpw7NuIGRlbCA1NS4yJSBlbnRyZSBqdWdhZG9yZXMgYW1lcmljYW5vcyB5IGFmcmljYW5vcywgY29udHJvbGFuZG8gcG9yIHJlbmRpbWllbnRvIHkgbGlnYSwgcmV2ZWxhIHVuIHNlc2dvIGRlIG1lcmNhZG8gc2lnbmlmaWNhdGl2by4gCkVzdGEgZGlzcGFyaWRhZCwgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YSAocC12YWx1ZSA9IDAuMDAwOCksIGluZGljYSB1bmEgaW5lZmljaWVuY2lhIGRlIG1lcmNhZG8gZXN0cnVjdHVyYWwgcXVlIHRyYXNjaWVuZGUgZWwgcmVuZGltaWVudG8gZGVwb3J0aXZvIHB1cm8uCgpMYXMgaW5lZmljaWVuY2lhcyBpZGVudGlmaWNhZGFzIGVuIGVsIG1lcmNhZG8gc3VnaWVyZW4gcXVlIGVsIHZhbG9yIHJlYWwgZGUgdW4ganVnYWRvciBwdWVkZSBkaWZlcmlyIHNpZ25pZmljYXRpdmFtZW50ZSBkZSBsYXMgdmFsb3JhY2lvbmVzIGRlIG1lcmNhZG8gYWN0dWFsZXMsIGVzcGVjaWFsbWVudGUgZW4gbGlnYXMgc2VjdW5kYXJpYXMgeSBtZXJjYWRvcyBlbWVyZ2VudGVzLiAKTGEgdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYSBkZWwgbW9kZWxvIHJldmVsYSBxdWUgZXN0YXMgZGlzY3JlcGFuY2lhcyBzaWd1ZW4gcGF0cm9uZXMgcHJlZGVjaWJsZXMgeSBleHBsb3RhYmxlcy4KCkVsIGFuw6FsaXNpcyB0YW1iacOpbiBzZcOxYWxhIHVuYSBldm9sdWNpw7NuIGVuIGxhIGVzdHJ1Y3R1cmEgZGVsIG1lcmNhZG8sIGRvbmRlIGxvcyBmYWN0b3JlcyB0cmFkaWNpb25hbGVzIGRlIHZhbG9yYWNpw7NuIGVzdMOhbiBzaWVuZG8gY29tcGxlbWVudGFkb3MgcG9yIG3DqXRyaWNhcyBtw6FzIHNvZmlzdGljYWRhcy4gCkxhIHNpZ25pZmljYXRpdmEgcHJpbWEgcG9yIGNyZWF0aXZpZGFkIHN1Z2llcmUgdW4gbWVyY2FkbyBxdWUgZXN0w6EgY29tZW56YW5kbyBhIHZhbG9yYXIgbcOhcyBhY2VydGFkYW1lbnRlIGxhcyBjb250cmlidWNpb25lcyB0w6FjdGljYXMgY29tcGxlamFzLgoKRXN0b3MgaGFsbGF6Z29zIGluZGljYW4gcXVlIGVsIG1lcmNhZG8gZGUgdHJhbnNmZXJlbmNpYXMsIGF1bnF1ZSBjYWRhIHZleiBtw6FzIHNvZmlzdGljYWRvLCBtYW50aWVuZSBpbmVmaWNpZW5jaWFzIGVzdHJ1Y3R1cmFsZXMgc2lnbmlmaWNhdGl2YXMuIApMYSBjb21iaW5hY2nDs24gZGUgc2VzZ29zIGdlb2dyw6FmaWNvcywgcHJpbWFzIGRlIGxpZ2EgeSB2YWxvcmFjacOzbiBkZSBoYWJpbGlkYWRlcyBlc3BlY8OtZmljYXMgY3JlYSB1biBwYW5vcmFtYSBjb21wbGVqbyBwZXJvIGFuYWzDrXRpY2FtZW50ZSBuYXZlZ2FibGUgcGFyYSBsYSBpZGVudGlmaWNhY2nDs24gZGUgdmFsb3IuCgojIEJpYmxpb2dyYWbDrWEKLSBEYXZpZGNhcmlib28uIChuLmQuKS4gRm9vdGJhbGwgRGF0YSBmcm9tIFRyYW5zZmVybWFya3QgW0RhdGEgc2V0XS4gS2FnZ2xlLiBodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2RhdmlkY2FyaWJvby9wbGF5ZXItc2NvcmVzIAotIE9ya3VuYWt0YXMuIChuLmQuKS4gUHJlbWllciBMZWFndWUgQWxsIFBsYXllcnMgU3RhdHMgMjMvMjQgW0RhdGEgc2V0XS4gS2FnZ2xlLiBodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL29ya3VuYWt0YXMvcHJlbWllci1sZWFndWUtYWxsLXBsYXllcnMtc3RhdHMtMjMyNCAKLSBBcHVudGUgZGUgUmVncmVzacOzbiBMaW5lYWwuIE1hcsOtYSBFdWdlbmlhIFN6cmV0dGVyIE5vc3RlLCBGYWN1bHRhZCBkZSBDaWVuY2lhcyBFeGFjdGFzIHkgTmF0dXJhbGVzLCBVbml2ZXJzaWRhZCBkZSBCdWVub3MgQWlyZXMsIEFnb3N0byAtIE9jdHVicmUgZGUgMjAxNy4gaHR0cDovL21hdGUuZG0udWJhLmFyL35tZXN6cmUvYXB1bnRlX3JlZ3Jlc2lvbl9saW5lYWxfc3pyZXR0ZXIucGRmCi0gQ2h1biBZdSAmIFdlaXhpbiBZYW8gKDIwMTYpLiBSb2J1c3QgbGluZWFyIHJlZ3Jlc3Npb246IEEgcmV2aWV3IGFuZCBjb21wYXJpc29uLiBDb21tdW5pY2F0aW9ucyBpbiBTdGF0aXN0aWNzIC0gU2ltdWxhdGlvbiBhbmQKQ29tcHV0YXRpb24sIGh0dHBzOi8vd3d3LnRhbmRmb25saW5lLmNvbS9kb2kvYWJzLzEwLjEwODAvMDM2MTA5MTguMjAxNi4xMjAyMjcxCi0gQ2xheSBGb3JkICgyMDE4KS4gSW50ZXJwcmV0aW5nIExvZyBUcmFuc2Zvcm1hdGlvbnMgaW4gYSBMaW5lYXIgTW9kZWwuIFVuaXZlcnNpdHkgb2YgVmlyZ2luaWEgTGlicmFyeS4gaHR0cHM6Ly9saWJyYXJ5LnZpcmdpbmlhLmVkdS9kYXRhL2FydGljbGVzL2ludGVycHJldGluZy1sb2ctdHJhbnNmb3JtYXRpb25zLWluLWEtbGluZWFyLW1vZGVsCgo=